From 53bf23178c9ec661156f072b6356c0de55505af7 Mon Sep 17 00:00:00 2001 From: tsteven4 Date: Sat, 24 Nov 2018 11:59:45 -0700 Subject: [PATCH] update shapelib to 1.4.1 (#274) --- shapelib/COPYING | 483 +++++++++++++++ shapelib/README.GPSBabel | 6 +- shapelib/dbf_api.html | 4 +- shapelib/dbfopen.c | 562 +++++++++++------ shapelib/safileio.c | 11 +- shapelib/shapefil.h | 354 +++++++---- shapelib/shapelib.html | 11 +- shapelib/shpopen.c | 1252 +++++++++++++++++++++++++++++--------- 8 files changed, 2069 insertions(+), 614 deletions(-) create mode 100644 shapelib/COPYING diff --git a/shapelib/COPYING b/shapelib/COPYING new file mode 100644 index 000000000..0b643ac83 --- /dev/null +++ b/shapelib/COPYING @@ -0,0 +1,483 @@ + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/shapelib/README.GPSBabel b/shapelib/README.GPSBabel index 5597a0fc7..73ead7014 100644 --- a/shapelib/README.GPSBabel +++ b/shapelib/README.GPSBabel @@ -1,8 +1,6 @@ -This is a subset of Shapelib v1.3.0 from http://shapelib.maptools.org/ +This is a subset of Shapelib v1.4.1 from http://shapelib.maptools.org/ -The source is largely unmodified. It's subsetted here only to reduce +The source is unmodified. It's subsetted here only to reduce the amount of size in our tree that it takes and to reduce ongoing merge maintenance. -shpopen.c: int32_t instead of int32 was used to eliminate clang warnings. -shpopen.c: include for MSVC. diff --git a/shapelib/dbf_api.html b/shapelib/dbf_api.html index f7a311bb3..0bd0da1b8 100644 --- a/shapelib/dbf_api.html +++ b/shapelib/dbf_api.html @@ -175,9 +175,7 @@ int DBFAddField( DBFHandle hDBF, const char * pszFieldName, The DBFAddField() function is used to add new fields to an existing xBase - file opened with DBFOpen(), or created with DBFCreate(). Note that fields - can only be added to xBase files with no records, though this is limitation - of this API, not of the file format.

+ file opened with DBFOpen(), or created with DBFCreate().

The DBFAddField() return value is the field number of the new field, or -1 if the addition of the field failed.

diff --git a/shapelib/dbfopen.c b/shapelib/dbfopen.c index 1f717d3d1..148e593a4 100644 --- a/shapelib/dbfopen.c +++ b/shapelib/dbfopen.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 fwarmerdam Exp $ + * $Id: dbfopen.c,v 1.92 2016-12-05 18:44:08 erouault Exp $ * * Project: Shapelib * Purpose: Implementation of .dbf access API documented in dbf_api.html. @@ -7,13 +7,14 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2013, Even Rouault * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -34,6 +35,33 @@ ****************************************************************************** * * $Log: dbfopen.c,v $ + * Revision 1.92 2016-12-05 18:44:08 erouault + * * dbfopen.c, shapefil.h: write DBF end-of-file character 0x1A by default. + * This behaviour can be controlled with the DBFSetWriteEndOfFileChar() + * function. + * + * Revision 1.91 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.90 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * * Revision 1.89 2011-07-24 05:59:25 fwarmerdam * minimize use of CPLError in favor of SAHooks.Error() * @@ -75,8 +103,8 @@ * * Revision 1.77 2007/12/15 20:25:21 bram * dbfopen.c now reads the Code Page information from the DBF file, and exports - * this information as a string through the DBFGetCodePage function. This is - * either the number from the LDID header field ("LDID/") or as the + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/") or as the * content of an accompanying .CPG file. When creating a DBF file, the code can * be set using DBFCreateEx. * @@ -163,13 +191,45 @@ #include #include -SHP_CVSID("$Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 fwarmerdam Exp $") +#ifdef USE_CPL +#include "cpl_string.h" +#else + +#if defined(_MSC_VER) +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif +#elif defined(WIN32) || defined(_WIN32) +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif + +#define CPLsprintf sprintf +#define CPLsnprintf snprintf +#endif + +SHP_CVSID("$Id: dbfopen.c,v 1.92 2016-12-05 18:44:08 erouault Exp $") #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif +/* File header size */ +#define XBASE_FILEHDR_SZ 32 + +#define HEADER_RECORD_TERMINATOR 0x0D + +/* See http://www.manmrk.net/tutorials/database/xbase/dbf.html */ +#define END_OF_FILE_CHARACTER 0x1A + +#ifdef USE_CPL +CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {} +#else +#define CPL_IGNORE_RET_VAL_INT(x) x +#endif + /************************************************************************/ /* SfRealloc() */ /* */ @@ -198,8 +258,7 @@ static void * SfRealloc( void * pMem, int nNewSize ) static void DBFWriteHeader(DBFHandle psDBF) { - unsigned char abyHeader[XBASE_FLDHDR_SZ]; - int i; + unsigned char abyHeader[XBASE_FILEHDR_SZ] = { 0 }; if( !psDBF->bNoHeader ) return; @@ -209,21 +268,18 @@ static void DBFWriteHeader(DBFHandle psDBF) /* -------------------------------------------------------------------- */ /* Initialize the file header information. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) - abyHeader[i] = 0; - abyHeader[0] = 0x03; /* memo field? - just copying */ - /* write out a dummy date */ - abyHeader[1] = 95; /* YY */ - abyHeader[2] = 7; /* MM */ - abyHeader[3] = 26; /* DD */ + /* write out update date */ + abyHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900; + abyHeader[2] = (unsigned char) psDBF->nUpdateMonth; + abyHeader[3] = (unsigned char) psDBF->nUpdateDay; /* record count preset at zero */ abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256); abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256); - + abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256); abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256); @@ -234,20 +290,31 @@ static void DBFWriteHeader(DBFHandle psDBF) /* descriptions. */ /* -------------------------------------------------------------------- */ psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); - psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); - psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, + psDBF->sHooks.FWrite( abyHeader, XBASE_FILEHDR_SZ, 1, psDBF->fp ); + psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, psDBF->fp ); /* -------------------------------------------------------------------- */ /* Write out the newline character if there is room for it. */ /* -------------------------------------------------------------------- */ - if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) + if( psDBF->nHeaderLength > XBASE_FLDHDR_SZ*psDBF->nFields + + XBASE_FLDHDR_SZ ) { char cNewline; - cNewline = 0x0d; + cNewline = HEADER_RECORD_TERMINATOR; psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp ); } + +/* -------------------------------------------------------------------- */ +/* If the file is new, add a EOF character. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nRecords == 0 && psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } } /************************************************************************/ @@ -265,21 +332,30 @@ static int DBFFlushRecord( DBFHandle psDBF ) { psDBF->bCurrentRecordModified = FALSE; - nRecordOffset = - psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + psDBF->nHeaderLength; - if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 - || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, - psDBF->nRecordLength, + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 + || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, + psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) { char szMessage[128]; - sprintf( szMessage, "Failure writing DBF record %d.", + snprintf( szMessage, sizeof(szMessage), "Failure writing DBF record %d.", psDBF->nCurrentRecord ); psDBF->sHooks.Error( szMessage ); return FALSE; } + + if( psDBF->nCurrentRecord == psDBF->nRecords - 1 ) + { + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + } } return TRUE; @@ -299,23 +375,23 @@ static int DBFLoadRecord( DBFHandle psDBF, int iRecord ) if( !DBFFlushRecord( psDBF ) ) return FALSE; - nRecordOffset = + nRecordOffset = psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 ) { char szMessage[128]; - sprintf( szMessage, "fseek(%ld) failed on DBF file.\n", + snprintf( szMessage, sizeof(szMessage), "fseek(%ld) failed on DBF file.", (long) nRecordOffset ); psDBF->sHooks.Error( szMessage ); return FALSE; } - if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, + if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) { char szMessage[128]; - sprintf( szMessage, "fread(%d) failed on DBF file.\n", + snprintf( szMessage, sizeof(szMessage), "fread(%d) failed on DBF file.", psDBF->nRecordLength ); psDBF->sHooks.Error( szMessage ); return FALSE; @@ -335,33 +411,49 @@ void SHPAPI_CALL DBFUpdateHeader( DBFHandle psDBF ) { - unsigned char abyFileHeader[32]; + unsigned char abyFileHeader[XBASE_FILEHDR_SZ]; if( psDBF->bNoHeader ) DBFWriteHeader( psDBF ); - DBFFlushRecord( psDBF ); + if( !DBFFlushRecord( psDBF ) ) + return; psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); - psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp ); - + psDBF->sHooks.FRead( abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp ); + + abyFileHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900; + abyFileHeader[2] = (unsigned char) psDBF->nUpdateMonth; + abyFileHeader[3] = (unsigned char) psDBF->nUpdateDay; abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256); abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256); abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256); abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256); - + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); - psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp ); + psDBF->sHooks.FWrite( abyFileHeader, sizeof(abyFileHeader), 1, psDBF->fp ); psDBF->sHooks.FFlush( psDBF->fp ); } +/************************************************************************/ +/* DBFSetLastModifiedDate() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD ) +{ + psDBF->nUpdateYearSince1900 = nYYSince1900; + psDBF->nUpdateMonth = nMM; + psDBF->nUpdateDay = nDD; +} + /************************************************************************/ /* DBFOpen() */ /* */ /* Open a .dbf file. */ /************************************************************************/ - + DBFHandle SHPAPI_CALL DBFOpen( const char * pszFilename, const char * pszAccess ) @@ -378,7 +470,7 @@ DBFOpen( const char * pszFilename, const char * pszAccess ) /* */ /* Open a .dbf file. */ /************************************************************************/ - + DBFHandle SHPAPI_CALL DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) @@ -389,18 +481,19 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) int nFields, nHeadLen, iField, i; char *pszBasename, *pszFullname; int nBufSize = 500; + size_t nFullnameLen; /* -------------------------------------------------------------------- */ /* We only allow the access strings "rb" and "r+". */ /* -------------------------------------------------------------------- */ - if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 + if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 && strcmp(pszAccess,"r+b") != 0 ) return( NULL ); if( strcmp(pszAccess,"r") == 0 ) pszAccess = "rb"; - + if( strcmp(pszAccess,"r+") == 0 ) pszAccess = "rb+"; @@ -410,7 +503,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszFilename)+5); strcpy( pszBasename, pszFilename ); - for( i = strlen(pszBasename)-1; + for( i = (int)strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} @@ -418,30 +511,31 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.dbf", pszBasename ); - + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.dbf", pszBasename ); + psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); psDBF->fp = psHooks->FOpen( pszFullname, pszAccess ); memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); if( psDBF->fp == NULL ) { - sprintf( pszFullname, "%s.DBF", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.DBF", pszBasename ); psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess ); } - sprintf( pszFullname, "%s.cpg", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.cpg", pszBasename ); pfCPG = psHooks->FOpen( pszFullname, "r" ); if( pfCPG == NULL ) { - sprintf( pszFullname, "%s.CPG", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.CPG", pszBasename ); pfCPG = psHooks->FOpen( pszFullname, "r" ); } free( pszBasename ); free( pszFullname ); - + if( psDBF->fp == NULL ) { free( psDBF ); @@ -457,7 +551,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) /* Read Table Header info */ /* -------------------------------------------------------------------- */ pabyBuf = (unsigned char *) malloc(nBufSize); - if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 ) + if( psDBF->sHooks.FRead( pabyBuf, XBASE_FILEHDR_SZ, 1, psDBF->fp ) != 1 ) { psDBF->sHooks.FClose( psDBF->fp ); if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); @@ -466,14 +560,16 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) return NULL; } - psDBF->nRecords = - pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; + DBFSetLastModifiedDate(psDBF, pabyBuf[1], pabyBuf[2], pabyBuf[3]); + + psDBF->nRecords = + pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + (pabyBuf[7] & 0x7f) *256*256*256; psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256; psDBF->iLanguageDriver = pabyBuf[29]; - if (nHeadLen < 32) + if (psDBF->nRecordLength == 0 || nHeadLen < XBASE_FILEHDR_SZ) { psDBF->sHooks.FClose( psDBF->fp ); if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); @@ -482,7 +578,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) return NULL; } - psDBF->nFields = nFields = (nHeadLen - 32) / 32; + psDBF->nFields = nFields = (nHeadLen - XBASE_FILEHDR_SZ) / XBASE_FLDHDR_SZ; psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength); @@ -507,7 +603,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) } if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 ) { - sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver ); + snprintf( (char *) pabyBuf, nBufSize, "LDID/%d", psDBF->iLanguageDriver ); psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1); strcpy( psDBF->pszCodePage, (char *) pabyBuf ); } @@ -515,12 +611,13 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ /* Read in Field Definitions */ /* -------------------------------------------------------------------- */ - + pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); psDBF->pszHeader = (char *) pabyBuf; - psDBF->sHooks.FSeek( psDBF->fp, 32, 0 ); - if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) + psDBF->sHooks.FSeek( psDBF->fp, XBASE_FILEHDR_SZ, 0 ); + if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-XBASE_FILEHDR_SZ, 1, + psDBF->fp ) != 1 ) { psDBF->sHooks.FClose( psDBF->fp ); free( pabyBuf ); @@ -538,7 +635,7 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) { unsigned char *pabyFInfo; - pabyFInfo = pabyBuf+iField*32; + pabyFInfo = pabyBuf+iField*XBASE_FLDHDR_SZ; if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) { @@ -564,10 +661,12 @@ DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) if( iField == 0 ) psDBF->panFieldOffset[iField] = 1; else - psDBF->panFieldOffset[iField] = + psDBF->panFieldOffset[iField] = psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; } + DBFSetWriteEndOfFileChar( psDBF, TRUE ); + return( psDBF ); } @@ -587,7 +686,7 @@ DBFClose(DBFHandle psDBF) if( psDBF->bNoHeader ) DBFWriteHeader( psDBF ); - DBFFlushRecord( psDBF ); + CPL_IGNORE_RET_VAL_INT(DBFFlushRecord( psDBF )); /* -------------------------------------------------------------------- */ /* Update last access date, and number of records if we have */ @@ -664,6 +763,7 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook char *pszFullname, *pszBasename; int i, ldid = -1; char chZero = '\0'; + size_t nFullnameLen; /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ @@ -671,7 +771,7 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszFilename)+5); strcpy( pszBasename, pszFilename ); - for( i = strlen(pszBasename)-1; + for( i = (int)strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} @@ -679,31 +779,33 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.dbf", pszBasename ); + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.dbf", pszBasename ); /* -------------------------------------------------------------------- */ /* Create the file. */ /* -------------------------------------------------------------------- */ fp = psHooks->FOpen( pszFullname, "wb" ); - if( fp == NULL ) { - free(pszBasename); - free(pszFullname); + if( fp == NULL ) + { + free( pszBasename ); + free( pszFullname ); return( NULL ); } - + psHooks->FWrite( &chZero, 1, 1, fp ); psHooks->FClose( fp ); fp = psHooks->FOpen( pszFullname, "rb+" ); - if( fp == NULL ) { - free(pszBasename); - free(pszFullname); + if( fp == NULL ) + { + free( pszBasename ); + free( pszFullname ); return( NULL ); } - - sprintf( pszFullname, "%s.cpg", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.cpg", pszBasename ); if( pszCodePage != NULL ) { if( strncmp( pszCodePage, "LDID/", 5 ) == 0 ) @@ -737,8 +839,8 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook psDBF->nRecords = 0; psDBF->nFields = 0; psDBF->nRecordLength = 1; - psDBF->nHeaderLength = 33; - + psDBF->nHeaderLength = XBASE_FILEHDR_SZ + 1; /* + 1 for HEADER_RECORD_TERMINATOR */ + psDBF->panFieldOffset = NULL; psDBF->panFieldSize = NULL; psDBF->panFieldDecimals = NULL; @@ -758,6 +860,9 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 ); strcpy( psDBF->pszCodePage, pszCodePage ); } + DBFSetLastModifiedDate(psDBF, 95, 7, 26); /* dummy date */ + + DBFSetWriteEndOfFileChar(psDBF, TRUE); return( psDBF ); } @@ -769,7 +874,7 @@ DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHook /************************************************************************/ int SHPAPI_CALL -DBFAddField(DBFHandle psDBF, const char * pszFieldName, +DBFAddField(DBFHandle psDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals ) { @@ -782,7 +887,7 @@ DBFAddField(DBFHandle psDBF, const char * pszFieldName, else chNativeType = 'N'; - return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, + return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, nWidth, nDecimals ); } @@ -814,7 +919,7 @@ static char DBFGetNullCharacter(char chType) /************************************************************************/ int SHPAPI_CALL -DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, +DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, char chType, int nWidth, int nDecimals ) { @@ -829,14 +934,36 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, if( !DBFFlushRecord( psDBF ) ) return -1; + if( psDBF->nHeaderLength + XBASE_FLDHDR_SZ > 65535 ) + { + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), + "Cannot add field %s. Header length limit reached " + "(max 65535 bytes, 2046 fields).", + pszFieldName ); + psDBF->sHooks.Error( szMessage ); + return -1; + } + /* -------------------------------------------------------------------- */ /* Do some checking to ensure we can add records to this file. */ /* -------------------------------------------------------------------- */ if( nWidth < 1 ) return -1; - if( nWidth > 255 ) - nWidth = 255; + if( nWidth > XBASE_FLD_MAX_WIDTH ) + nWidth = XBASE_FLD_MAX_WIDTH; + + if( psDBF->nRecordLength + nWidth > 65535 ) + { + char szMessage[128]; + snprintf( szMessage, sizeof(szMessage), + "Cannot add field %s. Record length limit reached " + "(max 65535 bytes).", + pszFieldName ); + psDBF->sHooks.Error( szMessage ); + return -1; + } nOldRecordLength = psDBF->nRecordLength; nOldHeaderLength = psDBF->nHeaderLength; @@ -847,16 +974,16 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, /* -------------------------------------------------------------------- */ psDBF->nFields++; - psDBF->panFieldOffset = (int *) + psDBF->panFieldOffset = (int *) SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); - psDBF->panFieldSize = (int *) + psDBF->panFieldSize = (int *) SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); - psDBF->panFieldDecimals = (int *) + psDBF->panFieldDecimals = (int *) SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); - psDBF->pachFieldType = (char *) + psDBF->pachFieldType = (char *) SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); /* -------------------------------------------------------------------- */ @@ -871,20 +998,18 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, /* -------------------------------------------------------------------- */ /* Extend the required header information. */ /* -------------------------------------------------------------------- */ - psDBF->nHeaderLength += 32; + psDBF->nHeaderLength += XBASE_FLDHDR_SZ; psDBF->bUpdated = FALSE; - psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader, + psDBF->nFields*XBASE_FLDHDR_SZ); - pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); + pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * (psDBF->nFields-1); - for( i = 0; i < 32; i++ ) + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) pszFInfo[i] = '\0'; - if( (int) strlen(pszFieldName) < 10 ) - strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); - else - strncpy( pszFInfo, pszFieldName, 10); + strncpy( pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE ); pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; @@ -898,7 +1023,7 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, pszFInfo[16] = (unsigned char) nWidth; pszFInfo[17] = (unsigned char) nDecimals; } - + /* -------------------------------------------------------------------- */ /* Make the current record buffer appropriately larger. */ /* -------------------------------------------------------------------- */ @@ -936,6 +1061,17 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); } + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength; + + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + /* free record */ free(pszRecord); @@ -945,6 +1081,7 @@ DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, psDBF->nCurrentRecord = -1; psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; return( psDBF->nFields-1 ); } @@ -995,7 +1132,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ /* Extract the requested field. */ /* -------------------------------------------------------------------- */ - strncpy( psDBF->pszWorkField, + memcpy( psDBF->pszWorkField, ((const char *) pabyRec) + psDBF->panFieldOffset[iField], psDBF->panFieldSize[iField] ); psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0'; @@ -1005,11 +1142,17 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ /* Decode the field. */ /* -------------------------------------------------------------------- */ - if( chReqType == 'N' ) + if( chReqType == 'I' ) { - psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); + psDBF->fieldValue.nIntField = atoi(psDBF->pszWorkField); - pReturnField = &(psDBF->dfDoubleField); + pReturnField = &(psDBF->fieldValue.nIntField); + } + else if( chReqType == 'N' ) + { + psDBF->fieldValue.dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); + + pReturnField = &(psDBF->fieldValue.dfDoubleField); } /* -------------------------------------------------------------------- */ @@ -1032,7 +1175,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, *pchDst = '\0'; } #endif - + return( pReturnField ); } @@ -1046,14 +1189,14 @@ int SHPAPI_CALL DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) { - double *pdValue; + int *pnValue; - pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + pnValue = (int *) DBFReadAttribute( psDBF, iRecord, iField, 'I' ); - if( pdValue == NULL ) + if( pnValue == NULL ) return 0; else - return( (int) *pdValue ); + return( *pnValue ); } /************************************************************************/ @@ -1201,6 +1344,8 @@ DBFGetRecordCount( DBFHandle psDBF ) /* DBFGetFieldInfo() */ /* */ /* Return any requested information about the field. */ +/* pszFieldName must be at least XBASE_FLDNAME_LEN_READ+1 (=12) */ +/* bytes long. */ /************************************************************************/ DBFFieldType SHPAPI_CALL @@ -1221,20 +1366,21 @@ DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, { int i; - strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); - pszFieldName[11] = '\0'; - for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) + strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*XBASE_FLDHDR_SZ, + XBASE_FLDNAME_LEN_READ ); + pszFieldName[XBASE_FLDNAME_LEN_READ] = '\0'; + for( i = XBASE_FLDNAME_LEN_READ - 1; i > 0 && pszFieldName[i] == ' '; i-- ) pszFieldName[i] = '\0'; } if ( psDBF->pachFieldType[iField] == 'L' ) return( FTLogical); - else if( psDBF->pachFieldType[iField] == 'N' + else if( psDBF->pachFieldType[iField] == 'N' || psDBF->pachFieldType[iField] == 'F' ) { - if( psDBF->panFieldDecimals[iField] > 0 - || psDBF->panFieldSize[iField] > 10 ) + if( psDBF->panFieldDecimals[iField] > 0 + || psDBF->panFieldSize[iField] >= 10 ) return( FTDouble ); else return( FTInteger ); @@ -1257,7 +1403,7 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, { int i, j, nRetResult = TRUE; unsigned char *pabyRec; - char szSField[400], szFormat[20]; + char szSField[XBASE_FLD_MAX_WIDTH+1], szFormat[20]; /* -------------------------------------------------------------------- */ /* Is this a valid record? */ @@ -1316,46 +1462,27 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, case 'D': case 'N': case 'F': - if( psDBF->panFieldDecimals[iField] == 0 ) - { - int nWidth = psDBF->panFieldSize[iField]; + { + int nWidth = psDBF->panFieldSize[iField]; - if( (int) sizeof(szSField)-2 < nWidth ) - nWidth = sizeof(szSField)-2; + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; - sprintf( szFormat, "%%%dd", nWidth ); - sprintf(szSField, szFormat, (int) *((double *) pValue) ); - if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) - { - szSField[psDBF->panFieldSize[iField]] = '\0'; - nRetResult = FALSE; - } - - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - szSField, strlen(szSField) ); - } - else - { - int nWidth = psDBF->panFieldSize[iField]; - - if( (int) sizeof(szSField)-2 < nWidth ) - nWidth = sizeof(szSField)-2; - - sprintf( szFormat, "%%%d.%df", - nWidth, psDBF->panFieldDecimals[iField] ); - sprintf(szSField, szFormat, *((double *) pValue) ); - if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) - { - szSField[psDBF->panFieldSize[iField]] = '\0'; - nRetResult = FALSE; - } - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - szSField, strlen(szSField) ); - } - break; + snprintf( szFormat, sizeof(szFormat), "%%%d.%df", + nWidth, psDBF->panFieldDecimals[iField] ); + CPLsnprintf(szSField, sizeof(szSField), szFormat, *((double *) pValue) ); + if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + break; + } case 'L': - if (psDBF->panFieldSize[iField] >= 1 && + if (psDBF->panFieldSize[iField] >= 1 && (*(char*)pValue == 'F' || *(char*)pValue == 'T')) *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; break; @@ -1370,7 +1497,7 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, { memset( pabyRec+psDBF->panFieldOffset[iField], ' ', psDBF->panFieldSize[iField] ); - j = strlen((char *) pValue); + j = (int)strlen((char *) pValue); } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), @@ -1439,7 +1566,7 @@ DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, { memset( pabyRec+psDBF->panFieldOffset[iField], ' ', psDBF->panFieldSize[iField] ); - j = strlen((char *) pValue); + j = (int)strlen((char *) pValue); } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), @@ -1603,21 +1730,24 @@ DBFReadTuple(DBFHandle psDBF, int hEntity ) /************************************************************************/ DBFHandle SHPAPI_CALL -DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) +DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) { DBFHandle newDBF; newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage ); - if ( newDBF == NULL ) return ( NULL ); - + if ( newDBF == NULL ) return ( NULL ); + newDBF->nFields = psDBF->nFields; newDBF->nRecordLength = psDBF->nRecordLength; newDBF->nHeaderLength = psDBF->nHeaderLength; - - newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength ); - memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength ); - - newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); + + if( psDBF->pszHeader ) + { + newDBF->pszHeader = (char *) malloc ( XBASE_FLDHDR_SZ * psDBF->nFields ); + memcpy ( newDBF->pszHeader, psDBF->pszHeader, XBASE_FLDHDR_SZ * psDBF->nFields ); + } + + newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); @@ -1628,11 +1758,13 @@ DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) newDBF->bNoHeader = TRUE; newDBF->bUpdated = TRUE; - + newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar; + DBFWriteHeader ( newDBF ); DBFClose ( newDBF ); - + newDBF = DBFOpen ( pszFilename, "rb+" ); + newDBF->bWriteEndOfFileChar = psDBF->bWriteEndOfFileChar; return ( newDBF ); } @@ -1665,9 +1797,9 @@ DBFGetNativeFieldType( DBFHandle psDBF, int iField ) static void str_to_upper (char *string) { int len; - short i = -1; + int i = -1; - len = strlen (string); + len = (int)strlen (string); while (++i < len) if (isalpha(string[i]) && islower(string[i])) @@ -1686,20 +1818,23 @@ int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) { - char name[12], name1[12], name2[12]; + char name[XBASE_FLDNAME_LEN_READ+1], + name1[XBASE_FLDNAME_LEN_READ+1], + name2[XBASE_FLDNAME_LEN_READ+1]; int i; - strncpy(name1, pszFieldName,11); - name1[11] = '\0'; + strncpy(name1, pszFieldName,XBASE_FLDNAME_LEN_READ); + name1[XBASE_FLDNAME_LEN_READ] = '\0'; str_to_upper(name1); for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) { DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); - strncpy(name2,name,11); + strncpy(name2,name,XBASE_FLDNAME_LEN_READ); + name2[XBASE_FLDNAME_LEN_READ] = '\0'; str_to_upper(name2); - if(!strncmp(name1,name2,10)) + if(!strcmp(name1,name2)) return(i); } return(-1); @@ -1737,7 +1872,7 @@ int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ) /* DBFMarkRecordDeleted() */ /************************************************************************/ -int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, int bIsDeleted ) { @@ -1761,7 +1896,7 @@ int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, /* -------------------------------------------------------------------- */ if( bIsDeleted ) chNewFlag = '*'; - else + else chNewFlag = ' '; if( psDBF->pszCurrentRecord[0] != chNewFlag ) @@ -1826,28 +1961,29 @@ DBFDeleteField(DBFHandle psDBF, int iField) /* resize fields arrays */ psDBF->nFields--; - psDBF->panFieldOffset = (int *) + psDBF->panFieldOffset = (int *) SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); - psDBF->panFieldSize = (int *) + psDBF->panFieldSize = (int *) SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); - psDBF->panFieldDecimals = (int *) + psDBF->panFieldDecimals = (int *) SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); - psDBF->pachFieldType = (char *) + psDBF->pachFieldType = (char *) SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); /* update header information */ - psDBF->nHeaderLength -= 32; + psDBF->nHeaderLength -= XBASE_FLDHDR_SZ; psDBF->nRecordLength -= nDeletedFieldSize; /* overwrite field information in header */ - memmove(psDBF->pszHeader + iField*32, - psDBF->pszHeader + (iField+1)*32, - sizeof(char) * (psDBF->nFields - iField)*32); + memmove(psDBF->pszHeader + iField*XBASE_FLDHDR_SZ, + psDBF->pszHeader + (iField+1)*XBASE_FLDHDR_SZ, + sizeof(char) * (psDBF->nFields - iField)*XBASE_FLDHDR_SZ); - psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader, + psDBF->nFields*XBASE_FLDHDR_SZ); /* update size of current record appropriately */ psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, @@ -1867,14 +2003,14 @@ DBFDeleteField(DBFHandle psDBF, int iField) /* shift records to their new positions */ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) { - nRecordOffset = + nRecordOffset = nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength; /* load record */ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); - nRecordOffset = + nRecordOffset = psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; /* move record in two steps */ @@ -1886,6 +2022,12 @@ DBFDeleteField(DBFHandle psDBF, int iField) } + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + /* TODO: truncate file */ /* free record */ @@ -1893,6 +2035,7 @@ DBFDeleteField(DBFHandle psDBF, int iField) psDBF->nCurrentRecord = -1; psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; return TRUE; } @@ -1927,11 +2070,13 @@ DBFReorderFields( DBFHandle psDBF, int* panMap ) if( !DBFFlushRecord( psDBF ) ) return FALSE; - panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields); - panFieldSizeNew = (int *) malloc(sizeof(int) * psDBF->nFields); - panFieldDecimalsNew = (int *) malloc(sizeof(int) * psDBF->nFields); - pachFieldTypeNew = (char *) malloc(sizeof(char) * psDBF->nFields); - pszHeaderNew = (char*) malloc(sizeof(char) * 32 * psDBF->nFields); + /* a simple malloc() would be enough, but calloc() helps clang static analyzer */ + panFieldOffsetNew = (int *) calloc(sizeof(int), psDBF->nFields); + panFieldSizeNew = (int *) calloc(sizeof(int), psDBF->nFields); + panFieldDecimalsNew = (int *) calloc(sizeof(int), psDBF->nFields); + pachFieldTypeNew = (char *) calloc(sizeof(char), psDBF->nFields); + pszHeaderNew = (char*) malloc(sizeof(char) * XBASE_FLDHDR_SZ * + psDBF->nFields); /* shuffle fields definitions */ for(i=0; i < psDBF->nFields; i++) @@ -1939,8 +2084,8 @@ DBFReorderFields( DBFHandle psDBF, int* panMap ) panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]]; panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]]; pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]]; - memcpy(pszHeaderNew + i * 32, - psDBF->pszHeader + panMap[i] * 32, 32); + memcpy(pszHeaderNew + i * XBASE_FLDHDR_SZ, + psDBF->pszHeader + panMap[i] * XBASE_FLDHDR_SZ, XBASE_FLDHDR_SZ); } panFieldOffsetNew[0] = 1; for(i=1; i < psDBF->nFields; i++) @@ -2003,6 +2148,7 @@ DBFReorderFields( DBFHandle psDBF, int* panMap ) psDBF->nCurrentRecord = -1; psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; return TRUE; } @@ -2023,7 +2169,7 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, int nOffset; int nOldWidth; int nOldRecordLength; - int nRecordOffset; + SAOffset nRecordOffset; char* pszFInfo; char chOldType; int bIsNULL; @@ -2049,8 +2195,8 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, if( nWidth < 1 ) return -1; - if( nWidth > 255 ) - nWidth = 255; + if( nWidth > XBASE_FLD_MAX_WIDTH ) + nWidth = XBASE_FLD_MAX_WIDTH; /* -------------------------------------------------------------------- */ /* Assign the new field information fields. */ @@ -2062,15 +2208,12 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, /* -------------------------------------------------------------------- */ /* Update the header information. */ /* -------------------------------------------------------------------- */ - pszFInfo = psDBF->pszHeader + 32 * iField; + pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * iField; - for( i = 0; i < 32; i++ ) + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) pszFInfo[i] = '\0'; - if( (int) strlen(pszFieldName) < 10 ) - strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); - else - strncpy( pszFInfo, pszFieldName, 10); + strncpy( pszFInfo, pszFieldName, XBASE_FLDNAME_LEN_WRITE ); pszFInfo[11] = psDBF->pachFieldType[iField]; @@ -2111,6 +2254,7 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength); char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1)); + /* cppcheck-suppress uninitdata */ pszOldField[nOldWidth] = 0; /* move records to their new positions */ @@ -2157,6 +2301,18 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); } + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength; + + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + /* TODO: truncate file */ + free(pszRecord); free(pszOldField); } @@ -2165,6 +2321,7 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1)); + /* cppcheck-suppress uninitdata */ pszOldField[nOldWidth] = 0; /* move records to their new positions */ @@ -2216,12 +2373,33 @@ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); } + if( psDBF->bWriteEndOfFileChar ) + { + char ch = END_OF_FILE_CHARACTER; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nRecords + psDBF->nHeaderLength; + + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( &ch, 1, 1, psDBF->fp ); + } + free(pszRecord); free(pszOldField); } psDBF->nCurrentRecord = -1; psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; return TRUE; } + +/************************************************************************/ +/* DBFSetWriteEndOfFileChar() */ +/************************************************************************/ + +void SHPAPI_CALL DBFSetWriteEndOfFileChar( DBFHandle psDBF, int bWriteFlag ) +{ + psDBF->bWriteEndOfFileChar = bWriteFlag; +} diff --git a/shapelib/safileio.c b/shapelib/safileio.c index f3affe2ea..533e7ada3 100644 --- a/shapelib/safileio.c +++ b/shapelib/safileio.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: safileio.c,v 1.4 2008-01-16 20:05:14 bram Exp $ + * $Id: safileio.c,v 1.5 2016-12-05 12:44:05 erouault Exp $ * * Project: Shapelib * Purpose: Default implementation of file io based on stdio. @@ -9,7 +9,7 @@ * Copyright (c) 2007, Frank Warmerdam * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- @@ -34,6 +34,11 @@ ****************************************************************************** * * $Log: safileio.c,v $ + * Revision 1.5 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * * Revision 1.4 2008-01-16 20:05:14 bram * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use SASetupUtf8Hooks * tosetup the hooks and check SHPAPI_UTF8_HOOKS for its availability. Currently, this @@ -65,7 +70,7 @@ #include #include -SHP_CVSID("$Id: safileio.c,v 1.4 2008-01-16 20:05:14 bram Exp $"); +SHP_CVSID("$Id: safileio.c,v 1.5 2016-12-05 12:44:05 erouault Exp $"); #ifdef SHPAPI_UTF8_HOOKS # ifdef SHPAPI_WINDOWS diff --git a/shapelib/shapefil.h b/shapelib/shapefil.h index e90b1cc9d..08c645996 100644 --- a/shapelib/shapefil.h +++ b/shapelib/shapefil.h @@ -2,7 +2,7 @@ #define SHAPEFILE_H_INCLUDED /****************************************************************************** - * $Id: shapefil.h,v 1.52 2011-12-11 22:26:46 fwarmerdam Exp $ + * $Id: shapefil.h,v 1.55 2016-12-05 18:44:08 erouault Exp $ * * Project: Shapelib * Purpose: Primary include file for Shapelib. @@ -10,13 +10,14 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2016, Even Rouault * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -37,6 +38,33 @@ ****************************************************************************** * * $Log: shapefil.h,v $ + * Revision 1.55 2016-12-05 18:44:08 erouault + * * dbfopen.c, shapefil.h: write DBF end-of-file character 0x1A by default. + * This behaviour can be controlled with the DBFSetWriteEndOfFileChar() + * function. + * + * Revision 1.54 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.53 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * * Revision 1.52 2011-12-11 22:26:46 fwarmerdam * upgrade .qix access code to use SAHooks (gdal #3365) * @@ -146,6 +174,10 @@ #include #endif +#ifdef USE_CPL +#include "cpl_conv.h" +#endif + #ifdef __cplusplus extern "C" { #endif @@ -166,7 +198,7 @@ extern "C" { /* is disabled. */ /* -------------------------------------------------------------------- */ #define DISABLE_MULTIPATCH_MEASURE - + /* -------------------------------------------------------------------- */ /* SHPAPI_CALL */ /* */ @@ -190,7 +222,7 @@ extern "C" { /* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ /* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ /* */ -/* The complexity of the situtation is partly caused by the */ +/* The complexity of the situation is partly caused by the */ /* peculiar requirement of Visual C++ that __stdcall appear */ /* after any "*"'s in the return value of a function while the */ /* __declspec(dllexport) must appear before them. */ @@ -213,17 +245,17 @@ extern "C" { #ifndef SHPAPI_CALL1 # define SHPAPI_CALL1(x) x SHPAPI_CALL #endif - + /* -------------------------------------------------------------------- */ /* Macros for controlling CVSID and ensuring they don't appear */ /* as unreferenced variables resulting in lots of warnings. */ /* -------------------------------------------------------------------- */ #ifndef DISABLE_CVSID # if defined(__GNUC__) && __GNUC__ >= 4 -# define SHP_CVSID(string) static char cpl_cvsid[] __attribute__((used)) = string; +# define SHP_CVSID(string) static const char cpl_cvsid[] __attribute__((used)) = string; # else -# define SHP_CVSID(string) static char cpl_cvsid[] = string; \ -static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } +# define SHP_CVSID(string) static const char cpl_cvsid[] = string; \ +static const char *cvsid_aw() { return( cvsid_aw() ? NULL : cpl_cvsid ); } # endif #else # define SHP_CVSID(string) @@ -234,8 +266,8 @@ static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } /* UTF-8 encoded filenames Unicode filenames */ /* -------------------------------------------------------------------- */ #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -# define SHPAPI_WINDOWS -# define SHPAPI_UTF8_HOOKS +# define SHPAPI_WINDOWS +# define SHPAPI_UTF8_HOOKS #endif /* -------------------------------------------------------------------- */ @@ -269,29 +301,36 @@ void SHPAPI_CALL SASetupUtf8Hooks( SAHooks *psHooks ); /************************************************************************/ /* SHP Support. */ /************************************************************************/ -typedef struct +typedef struct tagSHPObject SHPObject; + +typedef struct { SAHooks sHooks; SAFile fpSHP; - SAFile fpSHX; + SAFile fpSHX; + + int nShapeType; /* SHPT_* */ - int nShapeType; /* SHPT_* */ - - unsigned int nFileSize; /* SHP file */ + unsigned int nFileSize; /* SHP file */ int nRecords; - int nMaxRecords; - unsigned int *panRecOffset; - unsigned int *panRecSize; + int nMaxRecords; + unsigned int*panRecOffset; + unsigned int *panRecSize; - double adBoundsMin[4]; - double adBoundsMax[4]; + double adBoundsMin[4]; + double adBoundsMax[4]; - int bUpdated; + int bUpdated; unsigned char *pabyRec; int nBufSize; + + int bFastModeReadObject; + unsigned char *pabyObjectBuf; + int nObjectBufSize; + SHPObject* psCachedObject; } SHPInfo; typedef SHPInfo * SHPHandle; @@ -299,66 +338,66 @@ typedef SHPInfo * SHPHandle; /* -------------------------------------------------------------------- */ /* Shape types (nSHPType) */ /* -------------------------------------------------------------------- */ -#define SHPT_NULL 0 -#define SHPT_POINT 1 -#define SHPT_ARC 3 -#define SHPT_POLYGON 5 -#define SHPT_MULTIPOINT 8 -#define SHPT_POINTZ 11 -#define SHPT_ARCZ 13 -#define SHPT_POLYGONZ 15 +#define SHPT_NULL 0 +#define SHPT_POINT 1 +#define SHPT_ARC 3 +#define SHPT_POLYGON 5 +#define SHPT_MULTIPOINT 8 +#define SHPT_POINTZ 11 +#define SHPT_ARCZ 13 +#define SHPT_POLYGONZ 15 #define SHPT_MULTIPOINTZ 18 -#define SHPT_POINTM 21 -#define SHPT_ARCM 23 -#define SHPT_POLYGONM 25 +#define SHPT_POINTM 21 +#define SHPT_ARCM 23 +#define SHPT_POLYGONM 25 #define SHPT_MULTIPOINTM 28 #define SHPT_MULTIPATCH 31 - /* -------------------------------------------------------------------- */ /* Part types - everything but SHPT_MULTIPATCH just uses */ /* SHPP_RING. */ /* -------------------------------------------------------------------- */ -#define SHPP_TRISTRIP 0 -#define SHPP_TRIFAN 1 -#define SHPP_OUTERRING 2 -#define SHPP_INNERRING 3 -#define SHPP_FIRSTRING 4 -#define SHPP_RING 5 +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 /* -------------------------------------------------------------------- */ /* SHPObject - represents on shape (without attributes) read */ /* from the .shp file. */ /* -------------------------------------------------------------------- */ -typedef struct +struct tagSHPObject { - int nSHPType; + int nSHPType; + + int nShapeId; /* -1 is unknown/unassigned */ - int nShapeId; /* -1 is unknown/unassigned */ + int nParts; + int *panPartStart; + int *panPartType; - int nParts; - int *panPartStart; - int *panPartType; - - int nVertices; - double *padfX; - double *padfY; - double *padfZ; - double *padfM; + int nVertices; + double *padfX; + double *padfY; + double *padfZ; + double *padfM; - double dfXMin; - double dfYMin; - double dfZMin; - double dfMMin; + double dfXMin; + double dfYMin; + double dfZMin; + double dfMMin; - double dfXMax; - double dfYMax; - double dfZMax; - double dfMMax; + double dfXMax; + double dfYMax; + double dfZMax; + double dfMMax; - int bMeasureIsUsed; -} SHPObject; + int bMeasureIsUsed; + int bFastModeReadObject; +}; /* -------------------------------------------------------------------- */ /* SHP API Prototypes */ @@ -369,8 +408,22 @@ typedef struct SHPHandle SHPAPI_CALL SHPOpen( const char * pszShapeFile, const char * pszAccess ); SHPHandle SHPAPI_CALL - SHPOpenLL( const char *pszShapeFile, const char *pszAccess, + SHPOpenLL( const char *pszShapeFile, const char *pszAccess, SAHooks *psHooks ); +SHPHandle SHPAPI_CALL + SHPOpenLLEx( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks, int bRestoreSHX ); + +int SHPAPI_CALL + SHPRestoreSHX( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks ); + +/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ +/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ +/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ +/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ +void SHPAPI_CALL SHPSetFastModeReadObject( SHPHandle hSHP, int bFastMode ); + SHPHandle SHPAPI_CALL SHPCreate( const char * pszShapeFile, int nShapeType ); SHPHandle SHPAPI_CALL @@ -390,15 +443,15 @@ void SHPAPI_CALL void SHPAPI_CALL SHPComputeExtents( SHPObject * psObject ); SHPObject SHPAPI_CALL1(*) - SHPCreateObject( int nSHPType, int nShapeId, int nParts, + SHPCreateObject( int nSHPType, int nShapeId, int nParts, const int * panPartStart, const int * panPartType, - int nVertices, + int nVertices, const double * padfX, const double * padfY, const double * padfZ, const double * padfM ); SHPObject SHPAPI_CALL1(*) SHPCreateSimpleObject( int nSHPType, int nVertices, - const double * padfX, - const double * padfY, + const double * padfX, + const double * padfY, const double * padfZ ); int SHPAPI_CALL @@ -417,7 +470,7 @@ const char SHPAPI_CALL1(*) /* -------------------------------------------------------------------- */ /* this can be two or four for binary or quad tree */ -#define MAX_SUBNODE 4 +#define MAX_SUBNODE 4 /* upper limit of tree levels for automatic estimation */ #define MAX_DEFAULT_TREE_DEPTH 12 @@ -425,62 +478,61 @@ const char SHPAPI_CALL1(*) typedef struct shape_tree_node { /* region covered by this node */ - double adfBoundsMin[4]; - double adfBoundsMax[4]; + double adfBoundsMin[4]; + double adfBoundsMax[4]; /* list of shapes stored at this node. The papsShapeObj pointers or the whole list can be NULL */ - int nShapeCount; - int *panShapeIds; + int nShapeCount; + int *panShapeIds; SHPObject **papsShapeObj; - int nSubNodes; + int nSubNodes; struct shape_tree_node *apsSubNode[MAX_SUBNODE]; - + } SHPTreeNode; typedef struct { SHPHandle hSHP; - - int nMaxDepth; - int nDimension; + + int nMaxDepth; + int nDimension; int nTotalCount; - - SHPTreeNode *psRoot; + + SHPTreeNode *psRoot; } SHPTree; SHPTree SHPAPI_CALL1(*) SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, double *padfBoundsMin, double *padfBoundsMax ); -void SHPAPI_CALL +void SHPAPI_CALL SHPDestroyTree( SHPTree * hTree ); -int SHPAPI_CALL +int SHPAPI_CALL SHPWriteTree( SHPTree *hTree, const char * pszFilename ); -int SHPAPI_CALL +int SHPAPI_CALL SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); -int SHPAPI_CALL +int SHPAPI_CALL SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); -void SHPAPI_CALL +void SHPAPI_CALL SHPTreeTrimExtraNodes( SHPTree * hTree ); -int SHPAPI_CALL1(*) +int SHPAPI_CALL1(*) SHPTreeFindLikelyShapes( SHPTree * hTree, double * padfBoundsMin, double * padfBoundsMax, int * ); -int SHPAPI_CALL +int SHPAPI_CALL SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); -int SHPAPI_CALL1(*) -SHPSearchDiskTree( FILE *fp, +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, int *pnShapeCount ); - typedef struct SHPDiskTreeInfo* SHPTreeDiskHandle; SHPTreeDiskHandle SHPAPI_CALL @@ -490,49 +542,86 @@ SHPTreeDiskHandle SHPAPI_CALL void SHPAPI_CALL SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree ); -int SHPAPI_CALL1(*) -SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree, - double *padfBoundsMin, double *padfBoundsMax, - int *pnShapeCount ); +int SHPAPI_CALL1(*) +SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); int SHPAPI_CALL SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, SAHooks *psHooks ); +/* -------------------------------------------------------------------- */ +/* SBN Search API */ +/* -------------------------------------------------------------------- */ + +typedef struct SBNSearchInfo* SBNSearchHandle; + +SBNSearchHandle SHPAPI_CALL + SBNOpenDiskTree( const char* pszSBNFilename, + SAHooks *psHooks ); + +void SHPAPI_CALL + SBNCloseDiskTree( SBNSearchHandle hSBN ); + +int SHPAPI_CALL1(*) +SBNSearchDiskTree( SBNSearchHandle hSBN, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +int SHPAPI_CALL1(*) +SBNSearchDiskTreeInteger( SBNSearchHandle hSBN, + int bMinX, int bMinY, int bMaxX, int bMaxY, + int *pnShapeCount ); + +void SHPAPI_CALL SBNSearchFreeIds( int* panShapeId ); + /************************************************************************/ /* DBF Support. */ /************************************************************************/ -typedef struct +typedef struct { SAHooks sHooks; - SAFile fp; + SAFile fp; int nRecords; - int nRecordLength; - int nHeaderLength; - int nFields; - int *panFieldOffset; - int *panFieldSize; - int *panFieldDecimals; - char *pachFieldType; + int nRecordLength; /* Must fit on uint16 */ + int nHeaderLength; /* File header length (32) + field + descriptor length + spare space. + Must fit on uint16 */ + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; - char *pszHeader; + char *pszHeader; /* Field descriptors */ - int nCurrentRecord; - int bCurrentRecordModified; - char *pszCurrentRecord; + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; int nWorkFieldLength; char *pszWorkField; - - int bNoHeader; - int bUpdated; - double dfDoubleField; + int bNoHeader; + int bUpdated; + + union + { + double dfDoubleField; + int nIntField; + } fieldValue; int iLanguageDriver; char *pszCodePage; + + int nUpdateYearSince1900; /* 0-255 */ + int nUpdateMonth; /* 1-12 */ + int nUpdateDay; /* 1-31 */ + + int bWriteEndOfFileChar; /* defaults to TRUE */ } DBFInfo; typedef DBFInfo * DBFHandle; @@ -545,8 +634,14 @@ typedef enum { FTInvalid } DBFFieldType; -#define XBASE_FLDHDR_SZ 32 - +/* Field descriptor/header size */ +#define XBASE_FLDHDR_SZ 32 +/* Shapelib read up to 11 characters, even if only 10 should normally be used */ +#define XBASE_FLDNAME_LEN_READ 11 +/* On writing, we limit to 10 characters */ +#define XBASE_FLDNAME_LEN_WRITE 10 +/* Normally only 254 characters should be used. We tolerate 255 historically */ +#define XBASE_FLD_MAX_WIDTH 255 DBFHandle SHPAPI_CALL DBFOpen( const char * pszDBFFile, const char * pszAccess ); @@ -560,19 +655,19 @@ DBFHandle SHPAPI_CALL DBFHandle SHPAPI_CALL DBFCreateLL( const char * pszDBFFile, const char * pszCodePage, SAHooks *psHooks ); -int SHPAPI_CALL +int SHPAPI_CALL DBFGetFieldCount( DBFHandle psDBF ); -int SHPAPI_CALL +int SHPAPI_CALL DBFGetRecordCount( DBFHandle psDBF ); -int SHPAPI_CALL +int SHPAPI_CALL DBFAddField( DBFHandle hDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals ); -int SHPAPI_CALL +int SHPAPI_CALL DBFAddNativeFieldType( DBFHandle hDBF, const char * pszFieldName, char chType, int nWidth, int nDecimals ); -int SHPAPI_CALL +int SHPAPI_CALL DBFDeleteField( DBFHandle hDBF, int iField ); int SHPAPI_CALL @@ -583,25 +678,25 @@ int SHPAPI_CALL char chType, int nWidth, int nDecimals ); DBFFieldType SHPAPI_CALL - DBFGetFieldInfo( DBFHandle psDBF, int iField, + DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, int * pnWidth, int * pnDecimals ); int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); -int SHPAPI_CALL +int SHPAPI_CALL DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); -double SHPAPI_CALL +double SHPAPI_CALL DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); const char SHPAPI_CALL1(*) DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); const char SHPAPI_CALL1(*) DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); -int SHPAPI_CALL +int SHPAPI_CALL DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); int SHPAPI_CALL - DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, int nFieldValue ); int SHPAPI_CALL DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, @@ -614,7 +709,7 @@ int SHPAPI_CALL int SHPAPI_CALL DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, - const char lFieldValue); + const char lFieldValue); int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, void * pValue ); @@ -624,22 +719,27 @@ int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ); -int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, int bIsDeleted ); DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); - -void SHPAPI_CALL + +void SHPAPI_CALL DBFClose( DBFHandle hDBF ); void SHPAPI_CALL DBFUpdateHeader( DBFHandle hDBF ); -char SHPAPI_CALL +char SHPAPI_CALL DBFGetNativeFieldType( DBFHandle hDBF, int iField ); const char SHPAPI_CALL1(*) DBFGetCodePage(DBFHandle psDBF ); +void SHPAPI_CALL + DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD ); + +void SHPAPI_CALL DBFSetWriteEndOfFileChar( DBFHandle psDBF, int bWriteFlag ); + #ifdef __cplusplus } #endif diff --git a/shapelib/shapelib.html b/shapelib/shapelib.html index 2015283c1..19a433604 100644 --- a/shapelib/shapelib.html +++ b/shapelib/shapelib.html @@ -1,11 +1,11 @@ -Shapefile C Library V1.2 - +Shapefile C Library + -

Shapefile C Library V1.2

+

Shapefile C Library

Purpose

@@ -46,7 +46,7 @@ XXX.dbf - holds the attributes in xBase (dBase) format.

Download

Source code, and some other odds and ends can be downloaded from -http://download.osgeo.org/shapelib or http://shapelib.maptools.org/dl.

+http://download.osgeo.org/shapelib.

Shapelib is available for anonymous CVS access: @@ -230,6 +230,3 @@ MS Excel to calculate the area of polygons and export as shapefiles.

- - - diff --git a/shapelib/shpopen.c b/shapelib/shpopen.c index 480c1b8a4..f9d28c23d 100644 --- a/shapelib/shpopen.c +++ b/shapelib/shpopen.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $ + * $Id: shpopen.c,v 1.76 2017-09-10 10:11:36 erouault Exp $ * * Project: Shapelib * Purpose: Implementation of core Shapefile read/write functions. @@ -7,13 +7,14 @@ * ****************************************************************************** * Copyright (c) 1999, 2001, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault * * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * or at the option of the licensee under the LGPL (see COPYING). This * option is discussed in more detail in shapelib.html. * * -- - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -34,6 +35,33 @@ ****************************************************************************** * * $Log: shpopen.c,v $ + * Revision 1.76 2017-09-10 10:11:36 erouault + * * shpopen.c: resync with GDAL copy. Make sure to zero terminate all + * error messages. And fix regression regarding re-writing the last shape + * of a file (https://trac.osgeo.org/gdal/ticket/7031) + * + * Revision 1.75 2016-12-05 12:44:05 erouault + * * Major overhaul of Makefile build system to use autoconf/automake. + * + * * Warning fixes in contrib/ + * + * Revision 1.74 2016-12-04 15:30:15 erouault + * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with + * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo + * structures extended with new members. New functions: + * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX, + * SHPSetFastModeReadObject + * + * * sbnsearch.c: new file to implement original ESRI .sbn spatial + * index reading. (no write support). New functions: + * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree, + * SBNSearchDiskTreeInteger, SBNSearchFreeIds + * + * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates + * with new file and symbols. + * + * * commit: helper script to cvs commit + * * Revision 1.73 2012-01-24 22:33:01 fwarmerdam * fix memory leak on failure to open .shp (gdal #4410) * @@ -144,7 +172,7 @@ * avoid c++ comments * * Revision 1.38 2002/05/07 16:43:39 warmerda - * Removed debugging printf. + * Removed debugging printf() * * Revision 1.37 2002/04/10 17:35:22 warmerda * fixed bug in ring reversal code @@ -159,7 +187,7 @@ * move pabyRec into SHPInfo for thread safety * * Revision 1.33 2001/07/03 12:18:15 warmerda - * Improved cleanup if SHX not found, provied by Riccardo Cohen. + * Improved cleanup if SHX not found, provided by Riccardo Cohen. * * Revision 1.32 2001/06/22 01:58:07 warmerda * be more careful about establishing initial bounds in face of NULL shapes @@ -272,10 +300,9 @@ #include #include #include -// GPSBabel-local: add stdint for MSVC. -#include +#include -SHP_CVSID("$Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $") +SHP_CVSID("$Id: shpopen.c,v 1.76 2017-09-10 10:11:36 erouault Exp $") typedef unsigned char uchar; @@ -296,14 +323,33 @@ typedef unsigned int int32; # define MAX(a,b) ((a>b) ? a : b) #endif -#if defined(WIN32) || defined(_WIN32) +#ifndef USE_CPL +#if defined(_MSC_VER) +# if _MSC_VER < 1900 +# define snprintf _snprintf +# endif +#elif defined(WIN32) || defined(_WIN32) # ifndef snprintf # define snprintf _snprintf # endif #endif +#endif -static int bBigEndian; +#ifndef CPL_UNUSED +#if defined(__GNUC__) && __GNUC__ >= 4 +# define CPL_UNUSED __attribute((__unused__)) +#else +# define CPL_UNUSED +#endif +#endif +#if defined(CPL_LSB) +#define bBigEndian FALSE +#elif defined(CPL_MSB) +#define bBigEndian TRUE +#else +static int bBigEndian; +#endif /************************************************************************/ /* SwapWord() */ @@ -351,12 +397,12 @@ static void * SfRealloc( void * pMem, int nNewSize ) void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) { - uchar abyHeader[100]; + uchar abyHeader[100] = { 0 }; int i; int32 i32; double dValue; int32 *panSHX; - + if (psSHP->fpSHX == NULL) { psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed"); @@ -366,8 +412,6 @@ void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < 100; i++ ) - abyHeader[i] = 0; abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; @@ -375,11 +419,11 @@ void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) i32 = psSHP->nFileSize/2; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - + i32 = 1000; /* version */ ByteCopy( &i32, abyHeader+28, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+28 ); - + i32 = psSHP->nShapeType; /* shape type */ ByteCopy( &i32, abyHeader+32, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+32 ); @@ -419,10 +463,15 @@ void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) /* -------------------------------------------------------------------- */ /* Write .shp file header. */ /* -------------------------------------------------------------------- */ - if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 + if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 ) { - psSHP->sHooks.Error( "Failure writing .shp header" ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failure writing .shp header: %s", strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); return; } @@ -432,11 +481,17 @@ void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - - if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 ) { - psSHP->sHooks.Error( "Failure writing .shx header" ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failure writing .shx header: %s", strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); + return; } @@ -444,6 +499,11 @@ void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) /* Write out the .shx contents. */ /* -------------------------------------------------------------------- */ panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); + if( panSHX == NULL ) + { + psSHP->sHooks.Error( "Failure allocatin panSHX" ); + return; + } for( i = 0; i < psSHP->nRecords; i++ ) { @@ -453,10 +513,15 @@ void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); } - if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) + if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) != psSHP->nRecords ) { - psSHP->sHooks.Error( "Failure writing .shx contents" ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failure writing .shx contents: %s", strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); } free( panSHX ); @@ -489,18 +554,20 @@ SHPOpen( const char * pszLayer, const char * pszAccess ) /* Open the .shp and .shx files based on the basename of the */ /* files or either file name. */ /************************************************************************/ - + SHPHandle SHPAPI_CALL SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) { - char *pszFullname, *pszBasename; - SHPHandle psSHP; - - uchar *pabyBuf; - int i; - double dValue; - + char *pszFullname, *pszBasename; + SHPHandle psSHP; + + uchar *pabyBuf; + int i; + double dValue; + int bLazySHXLoading = FALSE; + size_t nFullnameLen; + /* -------------------------------------------------------------------- */ /* Ensure the access string is one of the legal ones. We */ /* ensure the result string indicates binary to avoid common */ @@ -510,19 +577,24 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) || strcmp(pszAccess,"r+") == 0 ) pszAccess = "r+b"; else + { + bLazySHXLoading = strchr(pszAccess, 'l') != NULL; pszAccess = "rb"; - + } + /* -------------------------------------------------------------------- */ -/* Establish the byte order on this machine. */ +/* Establish the byte order on this machine. */ /* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) i = 1; if( *((uchar *) &i) == 1 ) bBigEndian = FALSE; else bBigEndian = TRUE; +#endif /* -------------------------------------------------------------------- */ -/* Initialize the info structure. */ +/* Initialize the info structure. */ /* -------------------------------------------------------------------- */ psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); @@ -530,12 +602,12 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) ); /* -------------------------------------------------------------------- */ -/* Compute the base (layer) name. If there is any extension */ -/* on the passed in filename we will strip it off. */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszLayer)+5); strcpy( pszBasename, pszLayer ); - for( i = strlen(pszBasename)-1; + for( i = (int)strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} @@ -544,22 +616,24 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) pszBasename[i] = '\0'; /* -------------------------------------------------------------------- */ -/* Open the .shp and .shx files. Note that files pulled from */ -/* a PC to Unix with upper case filenames won't work! */ +/* Open the .shp and .shx files. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ /* -------------------------------------------------------------------- */ - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.shp", pszBasename ) ; + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.shp", pszBasename ) ; psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); if( psSHP->fpSHP == NULL ) { - sprintf( pszFullname, "%s.SHP", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.SHP", pszBasename ); psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); } - + if( psSHP->fpSHP == NULL ) { - char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256); - sprintf( pszMessage, "Unable to open %s.shp or %s.SHP.", + size_t nMessageLen = strlen(pszBasename)*2+256; + char *pszMessage = (char *) malloc(nMessageLen); + snprintf( pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.", pszBasename, pszBasename ); psHooks->Error( pszMessage ); free( pszMessage ); @@ -571,18 +645,20 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) return NULL; } - sprintf( pszFullname, "%s.shx", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.shx", pszBasename ); psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); if( psSHP->fpSHX == NULL ) { - sprintf( pszFullname, "%s.SHX", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.SHX", pszBasename ); psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); } - + if( psSHP->fpSHX == NULL ) { - char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256); - sprintf( pszMessage, "Unable to open %s.shx or %s.SHX.", + size_t nMessageLen = strlen(pszBasename)*2+256; + char *pszMessage = (char *) malloc(nMessageLen); + snprintf( pszMessage, nMessageLen, "Unable to open %s.shx or %s.SHX." + "Try --config SHAPE_RESTORE_SHX true to restore or create it", pszBasename, pszBasename ); psHooks->Error( pszMessage ); free( pszMessage ); @@ -598,48 +674,52 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) free( pszBasename ); /* -------------------------------------------------------------------- */ -/* Read the file size from the SHP file. */ +/* Read the file size from the SHP file. */ /* -------------------------------------------------------------------- */ pabyBuf = (uchar *) malloc(100); psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ); - psSHP->nFileSize = ((unsigned int)pabyBuf[24] * 256 * 256 * 256 - + (unsigned int)pabyBuf[25] * 256 * 256 - + (unsigned int)pabyBuf[26] * 256 - + (unsigned int)pabyBuf[27]) * 2; + psSHP->nFileSize = ((unsigned int)pabyBuf[24]<<24)|(pabyBuf[25]<<16)| + (pabyBuf[26]<<8)|pabyBuf[27]; + if( psSHP->nFileSize < UINT_MAX / 2 ) + psSHP->nFileSize *= 2; + else + psSHP->nFileSize = (UINT_MAX / 2) * 2; /* -------------------------------------------------------------------- */ /* Read SHX file Header info */ /* -------------------------------------------------------------------- */ - if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 - || pabyBuf[0] != 0 - || pabyBuf[1] != 0 - || pabyBuf[2] != 0x27 + if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 + || pabyBuf[0] != 0 + || pabyBuf[1] != 0 + || pabyBuf[2] != 0x27 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) { psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." ); psSHP->sHooks.FClose( psSHP->fpSHP ); psSHP->sHooks.FClose( psSHP->fpSHX ); + free( pabyBuf ); free( psSHP ); return( NULL ); } - psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 - + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; - psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; + psSHP->nRecords = pabyBuf[27]|(pabyBuf[26]<<8)|(pabyBuf[25]<<16)| + ((pabyBuf[24] & 0x7F)<<24); + psSHP->nRecords = (psSHP->nRecords - 50) / 4; psSHP->nShapeType = pabyBuf[32]; if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) { - char szError[200]; - - sprintf( szError, + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), "Record count in .shp header is %d, which seems\n" "unreasonable. Assuming header is corrupt.", psSHP->nRecords ); - psSHP->sHooks.Error( szError ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); psSHP->sHooks.FClose( psSHP->fpSHP ); psSHP->sHooks.FClose( psSHP->fpSHX ); free( psSHP ); @@ -648,6 +728,21 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) return( NULL ); } + /* If a lot of records are advertized, check that the file is big enough */ + /* to hold them */ + if( psSHP->nRecords >= 1024 * 1024 ) + { + SAOffset nFileSize; + psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 2 ); + nFileSize = psSHP->sHooks.FTell( psSHP->fpSHX ); + if( nFileSize > 100 && + nFileSize/2 < (SAOffset)(psSHP->nRecords * 4 + 50) ) + { + psSHP->nRecords = (int)((nFileSize - 100) / 8); + } + psSHP->sHooks.FSeek( psSHP->fpSHX, 100, 0 ); + } + /* -------------------------------------------------------------------- */ /* Read the bounds. */ /* -------------------------------------------------------------------- */ @@ -667,15 +762,15 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) memcpy( &dValue, pabyBuf+60, 8 ); psSHP->adBoundsMax[1] = dValue; - if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ + if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ memcpy( &dValue, pabyBuf+68, 8 ); psSHP->adBoundsMin[2] = dValue; - + if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); memcpy( &dValue, pabyBuf+76, 8 ); psSHP->adBoundsMax[2] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ + + if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ memcpy( &dValue, pabyBuf+84, 8 ); psSHP->adBoundsMin[3] = dValue; @@ -686,8 +781,8 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) free( pabyBuf ); /* -------------------------------------------------------------------- */ -/* Read the .shx file to get the offsets to each record in */ -/* the .shp file. */ +/* Read the .shx file to get the offsets to each record in */ +/* the .shp file. */ /* -------------------------------------------------------------------- */ psSHP->nMaxRecords = psSHP->nRecords; @@ -695,19 +790,23 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); psSHP->panRecSize = (unsigned int *) malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); - pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); + if( bLazySHXLoading ) + pabyBuf = NULL; + else + pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); if (psSHP->panRecOffset == NULL || psSHP->panRecSize == NULL || - pabyBuf == NULL) + (!bLazySHXLoading && pabyBuf == NULL)) { - char szError[200]; + char szErrorMsg[200]; - sprintf(szError, + snprintf( szErrorMsg, sizeof(szErrorMsg), "Not enough memory to allocate requested memory (nRecords=%d).\n" - "Probably broken SHP file", + "Probably broken SHP file", psSHP->nRecords ); - psSHP->sHooks.Error( szError ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); psSHP->sHooks.FClose( psSHP->fpSHP ); psSHP->sHooks.FClose( psSHP->fpSHX ); if (psSHP->panRecOffset) free( psSHP->panRecOffset ); @@ -717,15 +816,23 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) return( NULL ); } - if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) + if( bLazySHXLoading ) + { + memset(psSHP->panRecOffset, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + memset(psSHP->panRecSize, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + return( psSHP ); + } + + if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) != psSHP->nRecords ) { - char szError[200]; + char szErrorMsg[200]; - sprintf( szError, - "Failed to read all values for %d records in .shx file.", - psSHP->nRecords ); - psSHP->sHooks.Error( szError ); + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failed to read all values for %d records in .shx file: %s.", + psSHP->nRecords, strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); /* SHX is short or unreadable for some reason. */ psSHP->sHooks.FClose( psSHP->fpSHP ); @@ -737,7 +844,7 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) return( NULL ); } - + /* In read-only mode, we can close the SHX now */ if (strcmp(pszAccess, "rb") == 0) { @@ -747,7 +854,7 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) for( i = 0; i < psSHP->nRecords; i++ ) { - int32 nOffset, nLength; + unsigned int nOffset, nLength; memcpy( &nOffset, pabyBuf + i * 8, 4 ); if( !bBigEndian ) SwapWord( 4, &nOffset ); @@ -755,6 +862,30 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); if( !bBigEndian ) SwapWord( 4, &nLength ); + if( nOffset > (unsigned int)INT_MAX ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid offset for entity %d", i); + str[sizeof(str)-1] = '\0'; + + psSHP->sHooks.Error( str ); + SHPClose(psSHP); + free( pabyBuf ); + return NULL; + } + if( nLength > (unsigned int)(INT_MAX / 2 - 4) ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid length for entity %d", i); + str[sizeof(str)-1] = '\0'; + + psSHP->sHooks.Error( str ); + SHPClose(psSHP); + free( pabyBuf ); + return NULL; + } psSHP->panRecOffset[i] = nOffset*2; psSHP->panRecSize[i] = nLength*2; } @@ -763,6 +894,225 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) return( psSHP ); } +/************************************************************************/ +/* SHPOpenLLEx() */ +/* */ +/* Open the .shp and .shx files based on the basename of the */ +/* files or either file name. It generally invokes SHPRestoreSHX() */ +/* in case when bRestoreSHX equals true. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpenLLEx( const char * pszLayer, const char * pszAccess, SAHooks *psHooks, + int bRestoreSHX ) + +{ + if ( !bRestoreSHX ) return SHPOpenLL ( pszLayer, pszAccess, psHooks ); + else + { + if ( SHPRestoreSHX ( pszLayer, pszAccess, psHooks ) ) + { + return SHPOpenLL ( pszLayer, pszAccess, psHooks ); + } + } + + return( NULL ); +} + +/************************************************************************/ +/* SHPRestoreSHX() */ +/* */ +/* Restore .SHX file using associated .SHP file. */ +/* */ +/************************************************************************/ + +int SHPAPI_CALL +SHPRestoreSHX ( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) + +{ + char *pszFullname, *pszBasename; + SAFile fpSHP, fpSHX; + + + uchar *pabyBuf; + int i; + size_t nFullnameLen; + unsigned int nSHPFilesize; + + size_t nMessageLen; + char *pszMessage; + + unsigned int nCurrentRecordOffset = 0; + unsigned int nCurrentSHPOffset = 100; + size_t nRealSHXContentSize = 100; + + const char pszSHXAccess[] = "w+b"; + char *pabySHXHeader; + char abyReadedRecord[8]; + unsigned int niRecord = 0; + unsigned int nRecordLength = 0; + unsigned int nRecordOffset = 50; + +/* -------------------------------------------------------------------- */ +/* Ensure the access string is one of the legal ones. We */ +/* ensure the result string indicates binary to avoid common */ +/* problems on Windows. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 + || strcmp(pszAccess,"r+") == 0 ) + pszAccess = "r+b"; + else + { + pszAccess = "rb"; + } + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; +#endif + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = (int)strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the .shp file. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ +/* -------------------------------------------------------------------- */ + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.shp", pszBasename ) ; + fpSHP = psHooks->FOpen(pszFullname, pszAccess ); + if( fpSHP == NULL ) + { + snprintf( pszFullname, nFullnameLen, "%s.SHP", pszBasename ); + fpSHP = psHooks->FOpen(pszFullname, pszAccess ); + } + + if( fpSHP == NULL ) + { + nMessageLen = strlen(pszBasename)*2+256; + pszMessage = (char *) malloc(nMessageLen); + snprintf( pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.", + pszBasename, pszBasename ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + free( pszBasename ); + free( pszFullname ); + + return( 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Read the file size from the SHP file. */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(100); + psHooks->FRead( pabyBuf, 100, 1, fpSHP ); + + nSHPFilesize = ((unsigned int)pabyBuf[24]<<24)|(pabyBuf[25]<<16)| + (pabyBuf[26]<<8)|pabyBuf[27]; + if( nSHPFilesize < UINT_MAX / 2 ) + nSHPFilesize *= 2; + else + nSHPFilesize = (UINT_MAX / 2) * 2; + + snprintf( pszFullname, nFullnameLen, "%s.shx", pszBasename ); + fpSHX = psHooks->FOpen( pszFullname, pszSHXAccess ); + + if( fpSHX == NULL ) + { + nMessageLen = strlen( pszBasename ) * 2 + 256; + pszMessage = (char *) malloc( nMessageLen ); + snprintf( pszMessage, nMessageLen, "Error opening file %s.shx for writing", + pszBasename ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + psHooks->FClose( fpSHX ); + + free( pabyBuf ); + free( pszBasename ); + free( pszFullname ); + + return( 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Open SHX and create it using SHP file content. */ +/* -------------------------------------------------------------------- */ + psHooks->FSeek( fpSHP, 100, 0 ); + pabySHXHeader = (char *) malloc ( 100 ); + memcpy( pabySHXHeader, pabyBuf, 100 ); + psHooks->FWrite( pabySHXHeader, 100, 1, fpSHX ); + + while( nCurrentSHPOffset < nSHPFilesize ) + { + if( psHooks->FRead( &niRecord, 4, 1, fpSHP ) == 1 && + psHooks->FRead( &nRecordLength, 4, 1, fpSHP ) == 1) + { + if( !bBigEndian ) SwapWord( 4, &nRecordOffset ); + memcpy( abyReadedRecord, &nRecordOffset, 4 ); + memcpy( abyReadedRecord + 4, &nRecordLength, 4 ); + + psHooks->FWrite( abyReadedRecord, 8, 1, fpSHX ); + + if ( !bBigEndian ) SwapWord( 4, &nRecordOffset ); + if ( !bBigEndian ) SwapWord( 4, &nRecordLength ); + nRecordOffset += nRecordLength + 4; + nCurrentRecordOffset += 8; + nCurrentSHPOffset += 8 + nRecordLength * 2; + + psHooks->FSeek( fpSHP, nCurrentSHPOffset, 0 ); + nRealSHXContentSize += 8; + } + else + { + psHooks->Error( "Error parsing .shp to restore .shx" ); + + psHooks->FClose( fpSHX ); + psHooks->FClose( fpSHP ); + + free( pabySHXHeader ); + free( pszBasename ); + free( pszFullname ); + + return( 0 ); + } + } + + nRealSHXContentSize /= 2; // Bytes counted -> WORDs + if( !bBigEndian ) SwapWord( 4, &nRealSHXContentSize ); + psHooks->FSeek( fpSHX, 24, 0 ); + psHooks->FWrite( &nRealSHXContentSize, 4, 1, fpSHX ); + + psHooks->FClose( fpSHP ); + psHooks->FClose( fpSHX ); + + free ( pabyBuf ); + free ( pszFullname ); + free ( pszBasename ); + free ( pabySHXHeader ); + + return( 1 ); +} + /************************************************************************/ /* SHPClose() */ /* */ @@ -796,10 +1146,41 @@ SHPClose(SHPHandle psSHP ) { free( psSHP->pabyRec ); } - + + if( psSHP->pabyObjectBuf != NULL ) + { + free( psSHP->pabyObjectBuf ); + } + if( psSHP->psCachedObject != NULL ) + { + free( psSHP->psCachedObject ); + } + free( psSHP ); } +/************************************************************************/ +/* SHPSetFastModeReadObject() */ +/************************************************************************/ + +/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ +/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ +/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ +/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ +void SHPAPI_CALL SHPSetFastModeReadObject( SHPHandle hSHP, int bFastMode ) +{ + if( bFastMode ) + { + if( hSHP->psCachedObject == NULL ) + { + hSHP->psCachedObject = (SHPObject*) calloc(1, sizeof(SHPObject)); + assert( hSHP->psCachedObject != NULL ); + } + } + + hSHP->bFastModeReadObject = bFastMode; +} + /************************************************************************/ /* SHPGetInfo() */ /* */ @@ -815,7 +1196,7 @@ SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, if( psSHP == NULL ) return; - + if( pnEntities != NULL ) *pnEntities = psSHP->nRecords; @@ -866,15 +1247,18 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) uchar abyHeader[100]; int32 i32; double dValue; - + size_t nFullnameLen; + /* -------------------------------------------------------------------- */ /* Establish the byte order on this system. */ /* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) i = 1; if( *((uchar *) &i) == 1 ) bBigEndian = FALSE; else bBigEndian = TRUE; +#endif /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ @@ -882,7 +1266,7 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszLayer)+5); strcpy( pszBasename, pszLayer ); - for( i = strlen(pszBasename)-1; + for( i = (int)strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} @@ -893,20 +1277,30 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ /* Open the two files so we can write their headers. */ /* -------------------------------------------------------------------- */ - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.shp", pszBasename ); + nFullnameLen = strlen(pszBasename) + 5; + pszFullname = (char *) malloc(nFullnameLen); + snprintf( pszFullname, nFullnameLen, "%s.shp", pszBasename ); fpSHP = psHooks->FOpen(pszFullname, "wb" ); if( fpSHP == NULL ) { - psHooks->Error( "Failed to create file .shp file." ); + char szErrorMsg[200]; + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failed to create file %s: %s", + pszFullname, strerror(errno) ); + psHooks->Error( szErrorMsg ); + goto error; } - sprintf( pszFullname, "%s.shx", pszBasename ); + snprintf( pszFullname, nFullnameLen, "%s.shx", pszBasename ); fpSHX = psHooks->FOpen(pszFullname, "wb" ); if( fpSHX == NULL ) { - psHooks->Error( "Failed to create file .shx file." ); + char szErrorMsg[200]; + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failed to create file %s: %s", + pszFullname, strerror(errno) ); + psHooks->Error( szErrorMsg ); goto error; } @@ -916,8 +1310,7 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < 100; i++ ) - abyHeader[i] = 0; + memset( abyHeader, 0, sizeof(abyHeader) ); abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; @@ -925,11 +1318,11 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) i32 = 50; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - + i32 = 1000; /* version */ ByteCopy( &i32, abyHeader+28, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+28 ); - + i32 = nShapeType; /* shape type */ ByteCopy( &i32, abyHeader+32, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+32 ); @@ -945,7 +1338,13 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) /* -------------------------------------------------------------------- */ if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 ) { - psHooks->Error( "Failed to write .shp header." ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failed to write .shp header: %s", strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psHooks->Error( szErrorMsg ); + goto error; } @@ -955,10 +1354,16 @@ SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) i32 = 50; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - + if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 ) { - psHooks->Error( "Failed to write .shx header." ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Failure writing .shx header: %s", strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psHooks->Error( szErrorMsg ); + goto error; } @@ -1014,7 +1419,7 @@ SHPComputeExtents( SHPObject * psObject ) { int i; - + /* -------------------------------------------------------------------- */ /* Build extents for this object. */ /* -------------------------------------------------------------------- */ @@ -1025,7 +1430,7 @@ SHPComputeExtents( SHPObject * psObject ) psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; } - + for( i = 0; i < psObject->nVertices; i++ ) { psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); @@ -1106,10 +1511,10 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts, psObject->panPartStart[0] = 0; psObject->panPartType[0] = SHPP_RING; - + for( i = 0; i < nParts; i++ ) { - if( psObject->panPartStart != NULL ) + if( panPartStart != NULL ) psObject->panPartStart[i] = panPartStart[i]; if( panPartType != NULL ) @@ -1127,24 +1532,26 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts, /* -------------------------------------------------------------------- */ if( nVertices > 0 ) { - psObject->padfX = (double *) calloc(sizeof(double),nVertices); - psObject->padfY = (double *) calloc(sizeof(double),nVertices); - psObject->padfZ = (double *) calloc(sizeof(double),nVertices); - psObject->padfM = (double *) calloc(sizeof(double),nVertices); - - for( i = 0; i < nVertices; i++ ) - { - if( padfX != NULL ) - psObject->padfX[i] = padfX[i]; - if( padfY != NULL ) - psObject->padfY[i] = padfY[i]; - if( padfZ != NULL && bHasZ ) - psObject->padfZ[i] = padfZ[i]; - if( padfM != NULL && bHasM ) - psObject->padfM[i] = padfM[i]; - } + size_t nSize = sizeof(double) * nVertices; + psObject->padfX = (double *) padfX ? malloc(nSize) : + calloc(sizeof(double),nVertices); + psObject->padfY = (double *) padfY ? malloc(nSize) : + calloc(sizeof(double),nVertices); + psObject->padfZ = (double *) padfZ && bHasZ ? malloc(nSize) : + calloc(sizeof(double),nVertices); + psObject->padfM = (double *) padfM && bHasM ? malloc(nSize) : + calloc(sizeof(double),nVertices); + if( padfX != NULL ) + memcpy(psObject->padfX, padfX, nSize); + if( padfY != NULL ) + memcpy(psObject->padfY, padfY, nSize); + if( padfZ != NULL && bHasZ ) + memcpy(psObject->padfZ, padfZ, nSize); if( padfM != NULL && bHasM ) + { + memcpy(psObject->padfM, padfM, nSize); psObject->bMeasureIsUsed = TRUE; + } } /* -------------------------------------------------------------------- */ @@ -1172,7 +1579,7 @@ SHPCreateSimpleObject( int nSHPType, int nVertices, return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, nVertices, padfX, padfY, padfZ, NULL ) ); } - + /************************************************************************/ /* SHPWriteObject() */ /* */ @@ -1182,12 +1589,14 @@ SHPCreateSimpleObject( int nSHPType, int nVertices, int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) - + { unsigned int nRecordOffset, nRecordSize=0; int i; uchar *pabyRec; int32 i32; + int bAppendToLastRecord = FALSE; + int bAppendToFile = FALSE; psSHP->bUpdated = TRUE; @@ -1195,7 +1604,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* Ensure that shape object matches the type of the file it is */ /* being written to. */ /* -------------------------------------------------------------------- */ - assert( psObject->nSHPType == psSHP->nShapeType + assert( psObject->nSHPType == psSHP->nShapeType || psObject->nSHPType == SHPT_NULL ); /* -------------------------------------------------------------------- */ @@ -1203,7 +1612,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* assertion, or if they are disabled, set the shapeid to -1 */ /* for appends. */ /* -------------------------------------------------------------------- */ - assert( nShapeId == -1 + assert( nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) @@ -1214,33 +1623,45 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) { - psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); + int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100; + unsigned int* panRecOffsetNew; + unsigned int* panRecSizeNew; + + panRecOffsetNew = (unsigned int *) + SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * nNewMaxRecords ); + if( panRecOffsetNew == NULL ) + return -1; + psSHP->panRecOffset = panRecOffsetNew; + + panRecSizeNew = (unsigned int *) + SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * nNewMaxRecords ); + if( panRecSizeNew == NULL ) + return -1; + psSHP->panRecSize = panRecSizeNew; - psSHP->panRecOffset = (unsigned int *) - SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * psSHP->nMaxRecords ); - psSHP->panRecSize = (unsigned int *) - SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * psSHP->nMaxRecords ); + psSHP->nMaxRecords = nNewMaxRecords; } /* -------------------------------------------------------------------- */ /* Initialize record. */ /* -------------------------------------------------------------------- */ - pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8 + 128); - + if( pabyRec == NULL ) + return -1; + /* -------------------------------------------------------------------- */ /* Extract vertices for a Polygon or Arc. */ /* -------------------------------------------------------------------- */ if( psObject->nSHPType == SHPT_POLYGON || psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_POLYGONM - || psObject->nSHPType == SHPT_ARC + || psObject->nSHPType == SHPT_ARC || psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM || psObject->nSHPType == SHPT_MULTIPATCH ) { int32 nPoints, nParts; - int i; nPoints = psObject->nVertices; nParts = psObject->nParts; @@ -1290,7 +1711,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize + 8 ); @@ -1307,7 +1728,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; @@ -1326,16 +1747,16 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) if( psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARCM -#ifndef DISABLE_MULTIPATCH_MEASURE +#ifndef DISABLE_MULTIPATCH_MEASURE || psObject->nSHPType == SHPT_MULTIPATCH -#endif +#endif || psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_ARCZ) ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; @@ -1357,7 +1778,6 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) || psObject->nSHPType == SHPT_MULTIPOINTM ) { int32 nPoints; - int i; nPoints = psObject->nVertices; @@ -1365,7 +1785,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) if( bBigEndian ) SwapWord( 4, &nPoints ); ByteCopy( &nPoints, pabyRec + 44, 4 ); - + for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); @@ -1386,7 +1806,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); @@ -1406,7 +1826,7 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; - + for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); @@ -1430,14 +1850,14 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); nRecordSize = 28; - + if( psObject->nSHPType == SHPT_POINTZ ) { ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } - + if( psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ || psObject->nSHPType == SHPT_POINTM) ) @@ -1464,40 +1884,43 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ /* Establish where we are going to put this record. If we are */ -/* rewriting and existing record, and it will fit, then put it */ -/* back where the original came from. Otherwise write at the end. */ +/* rewriting the last record of the file, then we can update it in */ +/* place. Otherwise if rewriting an existing record, and it will */ +/* fit, then put it back where the original came from. Otherwise */ +/* write at the end. */ /* -------------------------------------------------------------------- */ - if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) + if( nShapeId != -1 && psSHP->panRecOffset[nShapeId] + + psSHP->panRecSize[nShapeId] + 8 == psSHP->nFileSize ) + { + nRecordOffset = psSHP->panRecOffset[nShapeId]; + bAppendToLastRecord = TRUE; + } + else if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) { - unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize; - if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow + if( psSHP->nFileSize > UINT_MAX - nRecordSize) { char str[128]; - sprintf( str, "Failed to write shape object. " + snprintf( str, sizeof(str), "Failed to write shape object. " "File size cannot reach %u + %u.", psSHP->nFileSize, nRecordSize ); + str[sizeof(str)-1] = '\0'; psSHP->sHooks.Error( str ); free( pabyRec ); return -1; } - if( nShapeId == -1 ) - nShapeId = psSHP->nRecords++; - - psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; - psSHP->panRecSize[nShapeId] = nRecordSize-8; - psSHP->nFileSize += nRecordSize; + bAppendToFile = TRUE; + nRecordOffset = psSHP->nFileSize; } else { nRecordOffset = psSHP->panRecOffset[nShapeId]; - psSHP->panRecSize[nShapeId] = nRecordSize-8; } - + /* -------------------------------------------------------------------- */ /* Set the shape type, record number, and record size. */ /* -------------------------------------------------------------------- */ - i32 = nShapeId+1; /* record # */ + i32 = (nShapeId < 0) ? psSHP->nRecords+1 : nShapeId+1; /* record # */ if( !bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec, 4 ); @@ -1514,19 +1937,47 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 ) { - psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() while writing object to .shp file." ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Error in psSHP->sHooks.FSeek() while writing object to .shp file: %s", + strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); + free( pabyRec ); return -1; } if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) { - psSHP->sHooks.Error( "Error in psSHP->sHooks.Fwrite() while writing object to .shp file." ); + char szErrorMsg[200]; + + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Error in psSHP->sHooks.FWrite() while writing object of %u bytes to .shp file: %s", + nRecordSize, strerror(errno) ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); + free( pabyRec ); return -1; } - + free( pabyRec ); + if( bAppendToLastRecord ) + { + psSHP->nFileSize = psSHP->panRecOffset[nShapeId] + nRecordSize; + } + else if( bAppendToFile ) + { + if( nShapeId == -1 ) + nShapeId = psSHP->nRecords++; + + psSHP->panRecOffset[nShapeId] = psSHP->nFileSize; + psSHP->nFileSize += nRecordSize; + } + psSHP->panRecSize[nShapeId] = nRecordSize-8; + /* -------------------------------------------------------------------- */ /* Expand file wide bounds based on this shape. */ /* -------------------------------------------------------------------- */ @@ -1546,8 +1997,8 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) { psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; - psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; - psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ ? psObject->padfZ[0] : 0.0; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM ? psObject->padfM[0] : 0.0; } } @@ -1555,17 +2006,68 @@ SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) { psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); - psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); - psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); - psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); - psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + if( psObject->padfZ ) + { + psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); + psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); + } + if( psObject->padfM ) + { + psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); + psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + } } return( nShapeId ); } +/************************************************************************/ +/* SHPAllocBuffer() */ +/************************************************************************/ + +static void* SHPAllocBuffer(unsigned char** pBuffer, int nSize) +{ + unsigned char* pRet; + + if( pBuffer == NULL ) + return calloc(1, nSize); + + pRet = *pBuffer; + if( pRet == NULL ) + return NULL; + + (*pBuffer) += nSize; + return pRet; +} + +/************************************************************************/ +/* SHPReallocObjectBufIfNecessary() */ +/************************************************************************/ + +static unsigned char* SHPReallocObjectBufIfNecessary ( SHPHandle psSHP, + int nObjectBufSize ) +{ + unsigned char* pBuffer; + if( nObjectBufSize == 0 ) + { + nObjectBufSize = 4 * sizeof(double); + } + if( nObjectBufSize > psSHP->nObjectBufSize ) + { + pBuffer = (unsigned char*) realloc( psSHP->pabyObjectBuf, nObjectBufSize ); + if( pBuffer != NULL ) + { + psSHP->pabyObjectBuf = pBuffer; + psSHP->nObjectBufSize = nObjectBufSize; + } + } + else + pBuffer = psSHP->pabyObjectBuf; + return pBuffer; +} + /************************************************************************/ /* SHPReadObject() */ /* */ @@ -1580,6 +2082,8 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) int nEntitySize, nRequiredSize; SHPObject *psShape; char szErrorMsg[128]; + int nSHPType; + int nBytesRead; /* -------------------------------------------------------------------- */ /* Validate the record/entity number. */ @@ -1587,29 +2091,113 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( hEntity < 0 || hEntity >= psSHP->nRecords ) return( NULL ); +/* -------------------------------------------------------------------- */ +/* Read offset/length from SHX loading if necessary. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != NULL ) + { + unsigned int nOffset, nLength; + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 100 + 8 * hEntity, 0 ) != 0 || + psSHP->sHooks.FRead( &nOffset, 1, 4, psSHP->fpSHX ) != 4 || + psSHP->sHooks.FRead( &nLength, 1, 4, psSHP->fpSHX ) != 4 ) + { + char str[128]; + snprintf( str, sizeof(str), + "Error in fseek()/fread() reading object from .shx file at offset %d", + 100 + 8 * hEntity); + str[sizeof(str)-1] = '\0'; + + psSHP->sHooks.Error( str ); + return NULL; + } + if( !bBigEndian ) SwapWord( 4, &nOffset ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + if( nOffset > (unsigned int)INT_MAX ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid offset for entity %d", hEntity); + str[sizeof(str)-1] = '\0'; + + psSHP->sHooks.Error( str ); + return NULL; + } + if( nLength > (unsigned int)(INT_MAX / 2 - 4) ) + { + char str[128]; + snprintf( str, sizeof(str), + "Invalid length for entity %d", hEntity); + str[sizeof(str)-1] = '\0'; + + psSHP->sHooks.Error( str ); + return NULL; + } + + psSHP->panRecOffset[hEntity] = nOffset*2; + psSHP->panRecSize[hEntity] = nLength*2; + } + /* -------------------------------------------------------------------- */ /* Ensure our record buffer is large enough. */ /* -------------------------------------------------------------------- */ nEntitySize = psSHP->panRecSize[hEntity]+8; if( nEntitySize > psSHP->nBufSize ) { - psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); - if (psSHP->pabyRec == NULL) + uchar* pabyRecNew; + int nNewBufSize = nEntitySize; + if( nNewBufSize < INT_MAX - nNewBufSize / 3 ) + nNewBufSize += nNewBufSize / 3; + else + nNewBufSize = INT_MAX; + + /* Before allocating too much memory, check that the file is big enough */ + /* and do not trust the file size in the header the first time we */ + /* need to allocate more than 10 MB */ + if( nNewBufSize >= 10 * 1024 * 1024 && + psSHP->nBufSize < 10 * 1024 * 1024 ) { - char szError[200]; + SAOffset nFileSize; + psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 2 ); + nFileSize = psSHP->sHooks.FTell(psSHP->fpSHP); + if( nFileSize >= UINT_MAX ) + psSHP->nFileSize = UINT_MAX; + else + psSHP->nFileSize = (unsigned int)nFileSize; + } - /* Reallocate previous successfull size for following features */ - psSHP->pabyRec = (uchar *) malloc(psSHP->nBufSize); + if( psSHP->panRecOffset[hEntity] >= psSHP->nFileSize || + /* We should normally use nEntitySize instead of*/ + /* psSHP->panRecSize[hEntity] in the below test, but because of */ + /* the case of non conformant .shx files detailed a bit below, */ + /* let be more tolerant */ + psSHP->panRecSize[hEntity] > psSHP->nFileSize - psSHP->panRecOffset[hEntity] ) + { + char str[128]; + snprintf( str, sizeof(str), + "Error in fread() reading object of size %d at offset %u from .shp file", + nEntitySize, psSHP->panRecOffset[hEntity] ); + str[sizeof(str)-1] = '\0'; - sprintf( szError, - "Not enough memory to allocate requested memory (nBufSize=%d). " - "Probably broken SHP file", psSHP->nBufSize ); - psSHP->sHooks.Error( szError ); + psSHP->sHooks.Error( str ); return NULL; } - /* Only set new buffer size after successfull alloc */ - psSHP->nBufSize = nEntitySize; + pabyRecNew = (uchar *) SfRealloc(psSHP->pabyRec,nNewBufSize); + if (pabyRecNew == NULL) + { + snprintf( szErrorMsg, sizeof(szErrorMsg), + "Not enough memory to allocate requested memory (nNewBufSize=%d). " + "Probably broken SHP file", nNewBufSize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + psSHP->sHooks.Error( szErrorMsg ); + return NULL; + } + + /* Only set new buffer size after successful alloc */ + psSHP->pabyRec = pabyRecNew; + psSHP->nBufSize = nNewBufSize; } /* In case we were not able to reallocate the buffer on a previous step */ @@ -1628,48 +2216,93 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) * for example to detect if file is truncated. */ char str[128]; - sprintf( str, + snprintf( str, sizeof(str), "Error in fseek() reading object from .shp file at offset %u", psSHP->panRecOffset[hEntity]); + str[sizeof(str)-1] = '\0'; psSHP->sHooks.Error( str ); return NULL; } - if( psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, psSHP->fpSHP ) != 1 ) + nBytesRead = (int)psSHP->sHooks.FRead( psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP ); + + /* Special case for a shapefile whose .shx content length field is not equal */ + /* to the content length field of the .shp, which is a violation of "The */ + /* content length stored in the index record is the same as the value stored in the main */ + /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */ + /* Actually in that case the .shx content length is equal to the .shp content length + */ + /* 4 (16 bit words), representing the 8 bytes of the record header... */ + if( nBytesRead >= 8 && nBytesRead == nEntitySize - 8 ) + { + /* Do a sanity check */ + int nSHPContentLength; + memcpy( &nSHPContentLength, psSHP->pabyRec + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &(nSHPContentLength) ); + if( nSHPContentLength < 0 || + nSHPContentLength > INT_MAX / 2 - 4 || + 2 * nSHPContentLength + 8 != nBytesRead ) + { + char str[128]; + snprintf( str, sizeof(str), + "Sanity check failed when trying to recover from inconsistent .shx/.shp with shape %d", + hEntity ); + str[sizeof(str)-1] = '\0'; + + psSHP->sHooks.Error( str ); + return NULL; + } + } + else if( nBytesRead != nEntitySize ) { /* * TODO - mloskot: Consider detailed diagnostics of shape file, * for example to detect if file is truncated. */ char str[128]; - sprintf( str, - "Error in fread() reading object of size %u at offset %u from .shp file", + snprintf( str, sizeof(str), + "Error in fread() reading object of size %d at offset %u from .shp file", nEntitySize, psSHP->panRecOffset[hEntity] ); + str[sizeof(str)-1] = '\0'; psSHP->sHooks.Error( str ); return NULL; } -/* -------------------------------------------------------------------- */ -/* Allocate and minimally initialize the object. */ -/* -------------------------------------------------------------------- */ - psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); - psShape->nShapeId = hEntity; - psShape->bMeasureIsUsed = FALSE; - if ( 8 + 4 > nEntitySize ) { snprintf(szErrorMsg, sizeof(szErrorMsg), "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); + hEntity, nEntitySize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); - SHPDestroyObject(psShape); return NULL; } - memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); + memcpy( &nSHPType, psSHP->pabyRec + 8, 4 ); - if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); + if( bBigEndian ) SwapWord( 4, &(nSHPType) ); + +/* -------------------------------------------------------------------- */ +/* Allocate and minimally initialize the object. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bFastModeReadObject ) + { + if( psSHP->psCachedObject->bFastModeReadObject ) + { + psSHP->sHooks.Error( "Invalid read pattern in fast read mode. " + "SHPDestroyObject() should be called." ); + return NULL; + } + + psShape = psSHP->psCachedObject; + memset(psShape, 0, sizeof(SHPObject)); + } + else + psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); + psShape->nShapeId = hEntity; + psShape->nSHPType = nSHPType; + psShape->bMeasureIsUsed = FALSE; + psShape->bFastModeReadObject = psSHP->bFastModeReadObject; /* ==================================================================== */ /* Extract vertices for a Polygon or Arc. */ @@ -1681,14 +2314,17 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) || psShape->nSHPType == SHPT_ARCM || psShape->nSHPType == SHPT_MULTIPATCH ) { - int32_t nPoints, nParts; + int32 nPoints, nParts; int i, nOffset; + unsigned char* pBuffer = NULL; + unsigned char** ppBuffer = NULL; if ( 40 + 8 + 4 > nEntitySize ) { snprintf(szErrorMsg, sizeof(szErrorMsg), "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); + hEntity, nEntitySize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; @@ -1716,17 +2352,19 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( bBigEndian ) SwapWord( 4, &nPoints ); if( bBigEndian ) SwapWord( 4, &nParts ); - if (nPoints < 0 || nParts < 0 || + /* nPoints and nParts are unsigned */ + if (/* nPoints < 0 || nParts < 0 || */ nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) { snprintf(szErrorMsg, sizeof(szErrorMsg), - "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", + "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u.", hEntity, nPoints, nParts); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - + /* With the previous checks on nPoints and nParts, */ /* we should not overflow here and after */ /* since 50 M * (16 + 8 + 8) = 1 600 MB */ @@ -1744,23 +2382,31 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if (nRequiredSize > nEntitySize) { snprintf(szErrorMsg, sizeof(szErrorMsg), - "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.", + "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u, nEntitySize=%d.", hEntity, nPoints, nParts, nEntitySize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } + if( psShape->bFastModeReadObject ) + { + int nObjectBufSize = 4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts; + pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); + ppBuffer = &pBuffer; + } + psShape->nVertices = nPoints; - psShape->padfX = (double *) calloc(nPoints,sizeof(double)); - psShape->padfY = (double *) calloc(nPoints,sizeof(double)); - psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); - psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + psShape->padfX = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfY = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfZ = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfM = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); psShape->nParts = nParts; - psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); - psShape->panPartType = (int *) calloc(nParts,sizeof(int)); - + psShape->panPartStart = (int *) SHPAllocBuffer(ppBuffer, nParts * sizeof(int)); + psShape->panPartType = (int *) SHPAllocBuffer(ppBuffer, nParts * sizeof(int)); + if (psShape->padfX == NULL || psShape->padfY == NULL || psShape->padfZ == NULL || @@ -1769,32 +2415,35 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) psShape->panPartType == NULL) { snprintf(szErrorMsg, sizeof(szErrorMsg), - "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. " - "Probably broken SHP file", hEntity, nPoints, nParts ); + "Not enough memory to allocate requested memory (nPoints=%u, nParts=%u) for shape %d. " + "Probably broken SHP file", nPoints, nParts, hEntity ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - for( i = 0; i < nParts; i++ ) + for( i = 0; (int32)i < nParts; i++ ) psShape->panPartType[i] = SHPP_RING; /* -------------------------------------------------------------------- */ /* Copy out the part array from the record. */ /* -------------------------------------------------------------------- */ memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); - for( i = 0; i < nParts; i++ ) + for( i = 0; (int32)i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); /* We check that the offset is inside the vertex array */ if (psShape->panPartStart[i] < 0 || (psShape->panPartStart[i] >= psShape->nVertices - && psShape->nVertices > 0) ) + && psShape->nVertices > 0) + || (psShape->panPartStart[i] > 0 && psShape->nVertices == 0) ) { snprintf(szErrorMsg, sizeof(szErrorMsg), "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", - hEntity, i, psShape->panPartStart[i], psShape->nVertices); + hEntity, i, psShape->panPartStart[i], psShape->nVertices); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; @@ -1803,7 +2452,8 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) { snprintf(szErrorMsg, sizeof(szErrorMsg), "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", - hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); + hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; @@ -1818,18 +2468,18 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( psShape->nSHPType == SHPT_MULTIPATCH ) { memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); - for( i = 0; i < nParts; i++ ) + for( i = 0; (int32)i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); } nOffset += 4*nParts; } - + /* -------------------------------------------------------------------- */ /* Copy out the vertices from the record. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < nPoints; i++ ) + for( i = 0; (int32)i < nPoints; i++ ) { memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, @@ -1844,7 +2494,7 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } nOffset += 16*nPoints; - + /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ @@ -1854,11 +2504,11 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) { memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -1867,6 +2517,10 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) nOffset += 16 + 8*nPoints; } + else if( psShape->bFastModeReadObject ) + { + psShape->padfZ = NULL; + } /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ @@ -1874,15 +2528,15 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ - if( nEntitySize >= nOffset + 16 + 8*nPoints ) + if( nEntitySize >= (int)(nOffset + 16 + 8*nPoints) ) { memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -1890,6 +2544,10 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } psShape->bMeasureIsUsed = TRUE; } + else if( psShape->bFastModeReadObject ) + { + psShape->padfM = NULL; + } } /* ==================================================================== */ @@ -1899,14 +2557,17 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) || psShape->nSHPType == SHPT_MULTIPOINTM || psShape->nSHPType == SHPT_MULTIPOINTZ ) { - int32_t nPoints; + int32 nPoints; int i, nOffset; + unsigned char* pBuffer = NULL; + unsigned char** ppBuffer = NULL; if ( 44 + 4 > nEntitySize ) { snprintf(szErrorMsg, sizeof(szErrorMsg), "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); + hEntity, nEntitySize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; @@ -1915,11 +2576,13 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( bBigEndian ) SwapWord( 4, &nPoints ); - if (nPoints < 0 || nPoints > 50 * 1000 * 1000) + /* nPoints is unsigned */ + if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000) { snprintf(szErrorMsg, sizeof(szErrorMsg), - "Corrupted .shp file : shape %d : nPoints = %d", - hEntity, nPoints); + "Corrupted .shp file : shape %d : nPoints = %u", + hEntity, nPoints); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; @@ -1933,18 +2596,27 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if (nRequiredSize > nEntitySize) { snprintf(szErrorMsg, sizeof(szErrorMsg), - "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", - hEntity, nPoints, nEntitySize); + "Corrupted .shp file : shape %d : nPoints = %u, nEntitySize = %d", + hEntity, nPoints, nEntitySize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - + + if( psShape->bFastModeReadObject ) + { + int nObjectBufSize = 4 * sizeof(double) * nPoints; + pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); + ppBuffer = &pBuffer; + } + psShape->nVertices = nPoints; - psShape->padfX = (double *) calloc(nPoints,sizeof(double)); - psShape->padfY = (double *) calloc(nPoints,sizeof(double)); - psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); - psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + psShape->padfX = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfY = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfZ = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfM = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); if (psShape->padfX == NULL || psShape->padfY == NULL || @@ -1952,14 +2624,15 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) psShape->padfM == NULL) { snprintf(szErrorMsg, sizeof(szErrorMsg), - "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. " - "Probably broken SHP file", hEntity, nPoints ); + "Not enough memory to allocate requested memory (nPoints=%u) for shape %d. " + "Probably broken SHP file", nPoints, hEntity ); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; } - for( i = 0; i < nPoints; i++ ) + for( i = 0; (int32)i < nPoints; i++ ) { memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); @@ -1969,7 +2642,7 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } nOffset = 48 + 16*nPoints; - + /* -------------------------------------------------------------------- */ /* Get the X/Y bounds. */ /* -------------------------------------------------------------------- */ @@ -1990,11 +2663,11 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) { memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -2003,6 +2676,8 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) nOffset += 16 + 8*nPoints; } + else if( psShape->bFastModeReadObject ) + psShape->padfZ = NULL; /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ @@ -2010,15 +2685,15 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ - if( nEntitySize >= nOffset + 16 + 8*nPoints ) + if( nEntitySize >= (int)(nOffset + 16 + 8*nPoints) ) { memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); - + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); - - for( i = 0; i < nPoints; i++ ) + + for( i = 0; (int32)i < nPoints; i++ ) { memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); @@ -2026,6 +2701,8 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) } psShape->bMeasureIsUsed = TRUE; } + else if( psShape->bFastModeReadObject ) + psShape->padfM = NULL; } /* ==================================================================== */ @@ -2036,18 +2713,31 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) || psShape->nSHPType == SHPT_POINTZ ) { int nOffset; - + psShape->nVertices = 1; - psShape->padfX = (double *) calloc(1,sizeof(double)); - psShape->padfY = (double *) calloc(1,sizeof(double)); - psShape->padfZ = (double *) calloc(1,sizeof(double)); - psShape->padfM = (double *) calloc(1,sizeof(double)); + if( psShape->bFastModeReadObject ) + { + psShape->padfX = &(psShape->dfXMin); + psShape->padfY = &(psShape->dfYMin); + psShape->padfZ = &(psShape->dfZMin); + psShape->padfM = &(psShape->dfMMin); + psShape->padfZ[0] = 0.0; + psShape->padfM[0] = 0.0; + } + else + { + psShape->padfX = (double *) calloc(1,sizeof(double)); + psShape->padfY = (double *) calloc(1,sizeof(double)); + psShape->padfZ = (double *) calloc(1,sizeof(double)); + psShape->padfM = (double *) calloc(1,sizeof(double)); + } if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize) { snprintf(szErrorMsg, sizeof(szErrorMsg), "Corrupted .shp file : shape %d : nEntitySize = %d", - hEntity, nEntitySize); + hEntity, nEntitySize); + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; psSHP->sHooks.Error( szErrorMsg ); SHPDestroyObject(psShape); return NULL; @@ -2059,16 +2749,16 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( bBigEndian ) SwapWord( 8, psShape->padfY ); nOffset = 20 + 8; - + /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ if( psShape->nSHPType == SHPT_POINTZ ) { memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); - + if( bBigEndian ) SwapWord( 8, psShape->padfZ ); - + nOffset += 8; } @@ -2081,7 +2771,7 @@ SHPReadObject( SHPHandle psSHP, int hEntity ) if( nEntitySize >= nOffset + 8 ) { memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); - + if( bBigEndian ) SwapWord( 8, psShape->padfM ); psShape->bMeasureIsUsed = TRUE; } @@ -2123,7 +2813,7 @@ SHPTypeName( int nSHPType ) case SHPT_MULTIPOINT: return "MultiPoint"; - + case SHPT_POINTZ: return "PointZ"; @@ -2135,7 +2825,7 @@ SHPTypeName( int nSHPType ) case SHPT_MULTIPOINTZ: return "MultiPointZ"; - + case SHPT_POINTM: return "PointM"; @@ -2168,7 +2858,7 @@ SHPPartTypeName( int nPartType ) { case SHPP_TRISTRIP: return "TriangleStrip"; - + case SHPP_TRIFAN: return "TriangleFan"; @@ -2199,7 +2889,13 @@ SHPDestroyObject( SHPObject * psShape ) { if( psShape == NULL ) return; - + + if( psShape->bFastModeReadObject ) + { + psShape->bFastModeReadObject = FALSE; + return; + } + if( psShape->padfX != NULL ) free( psShape->padfX ); if( psShape->padfY != NULL ) @@ -2225,8 +2921,8 @@ SHPDestroyObject( SHPObject * psShape ) /************************************************************************/ int SHPAPI_CALL -SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) - +SHPRewindObject( CPL_UNUSED SHPHandle hSHP, + SHPObject * psObject ) { int iOpRing, bAltered = 0; @@ -2273,14 +2969,14 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( iCheckRing == iOpRing ) continue; - + nVertStart = psObject->panPartStart[iCheckRing]; if( iCheckRing == psObject->nParts-1 ) - nVertCount = psObject->nVertices + nVertCount = psObject->nVertices - psObject->panPartStart[iCheckRing]; else - nVertCount = psObject->panPartStart[iCheckRing+1] + nVertCount = psObject->panPartStart[iCheckRing+1] - psObject->panPartStart[iCheckRing]; for( iEdge = 0; iEdge < nVertCount; iEdge++ ) @@ -2294,7 +2990,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) /* Rule #1: * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY) - * The rule #1 also excludes edges collinear with the ray. + * The rule #1 also excludes edges colinear with the ray. */ if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY && dfTestY <= psObject->padfY[iNext+nVertStart] ) @@ -2304,9 +3000,9 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) /* Rule #2: * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) */ - double const intersect = + double const intersect = ( psObject->padfX[iEdge+nVertStart] - + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) + + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] ) * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) ); @@ -2314,7 +3010,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) { bInner = !bInner; } - } + } } } /* for iCheckRing */ @@ -2327,7 +3023,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( iOpRing == psObject->nParts-1 ) nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; else - nVertCount = psObject->panPartStart[iOpRing+1] + nVertCount = psObject->panPartStart[iOpRing+1] - psObject->panPartStart[iOpRing]; if (nVertCount < 2) @@ -2355,13 +3051,13 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) /* Swap X */ dfSaved = psObject->padfX[nVertStart+i]; - psObject->padfX[nVertStart+i] = + psObject->padfX[nVertStart+i] = psObject->padfX[nVertStart+nVertCount-i-1]; psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; /* Swap Y */ dfSaved = psObject->padfY[nVertStart+i]; - psObject->padfY[nVertStart+i] = + psObject->padfY[nVertStart+i] = psObject->padfY[nVertStart+nVertCount-i-1]; psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; @@ -2369,7 +3065,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( psObject->padfZ ) { dfSaved = psObject->padfZ[nVertStart+i]; - psObject->padfZ[nVertStart+i] = + psObject->padfZ[nVertStart+i] = psObject->padfZ[nVertStart+nVertCount-i-1]; psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; } @@ -2378,7 +3074,7 @@ SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) if( psObject->padfM ) { dfSaved = psObject->padfM[nVertStart+i]; - psObject->padfM[nVertStart+i] = + psObject->padfM[nVertStart+i] = psObject->padfM[nVertStart+nVertCount-i-1]; psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; } -- 2.30.2